Du hast in beiden Definitionen kleine aber wesentliche Fehler. In der Tokenlist-Definition hast Du folgende Zeile:
\tl_set:Nn \l_zeichen_tl {\str_item_ignore_spaces:nn {#1} {##1}}
Wenn Du die damit definierte Tokenlist mit `\tl_show:N \l_zeichen_tl` inspizierst, zeigt es Dir z.B.
> \l_zeichen_tl=\str_item_ignore_spaces:nn {12ab}{1}
Ändere die Zeile in
\tl_set:Nx \l_zeichen_tl {\str_item_ignore_spaces:nn {#1} {##1}}
und daraus wird
> \l_zeichen_tl=1
Das reicht aber noch nicht. `\tl_case:Nn` testet gegen den Inhalt von Variablen, nicht gegen einzelne Token. Du musst also passende Testvariablen definieren
\tl_const:Nn \c_zeichen_case_a_tl {0}
\tl_const:Nn \c_zeichen_case_b_tl {1}
\tl_const:Nn \c_zeichen_case_c_tl {a}
\tl_const:Nn \c_zeichen_case_d_tl {b}
und verwenden:
\tl_case:NnF \l_zeichen_tl
{
\c_zeichen_case_a_tl {}
\c_zeichen_case_b_tl {}
\c_zeichen_case_c_tl {}
\c_zeichen_case_d_tl {}
}
{...}
Das reicht immer noch nicht: in Tokenlists hängt ein Match auch vom Kategoriecode der Zeichen ab. Mit
\tl_set:Nx \l_zeichen_tl {\str_item_ignore_spaces:nn {#1} {##1}}
ensteht eine Tokenliste, in der alle Zeichen Kategoriecode 12 haben. In den Testvariablen haben sie das mit obiger Definition nicht. Darum ändern wir noch mal:
\tl_set:Nx \l_zeichen_tl {\tl_item:nn {#1} {##1}}
Und jetzt klappt der Test.
----------
In der String-Variante hast Du ähnliche Fehler:
\str_set:Nn \l_zeichen_str {\str_item_ignore_spaces:nn {#1} {##1}}
Ändere das in
\str_set:Nx \l_zeichen_str {\str_item_ignore_spaces:nn {#1} {##1}}
Außerdem hast Du
\str_case:nnF {\l_zeichen_str} ...
Das vergleicht aber *nicht* den Inhalt der Variablen `\l_zeichen_str` mit den verschiedenen Fällen, sondern den String `\ l _ z e i c h e n _ s t r` (ohne Leerzeichen). Du brauchst die Variante
\str_case:VnF \l_zeichen_str ...
die Du am besten mit
\cs_generate_variant:Nn \str_case:nnF {V}
bereitstellst.
----------
Mit diesen Korrekturen funktioniert Dein Code:
\documentclass{article}
\usepackage[check-declarations]{expl3}
\usepackage{xparse}
\ExplSyntaxOn
\int_new:N \l_zeichenzahl_int
\tl_new:N \l_zeichen_tl
\str_new:N \l_zeichen_str
\tl_const:Nn \c_zeichen_case_a_tl {0}
\tl_const:Nn \c_zeichen_case_b_tl {1}
\tl_const:Nn \c_zeichen_case_c_tl {a}
\tl_const:Nn \c_zeichen_case_d_tl {b}
\cs_new_protected:Npn \test_mit_tl:n #1
{
\int_set:Nn \l_zeichenzahl_int {\str_count_ignore_spaces:n {#1}}
\cs_set:Npn \zeichen_pruefen:n ##1
{
\tl_set:Nx \l_zeichen_tl {\tl_item:nn {#1} {##1}}
\tl_case:NnF \l_zeichen_tl
{
\c_zeichen_case_a_tl {}
\c_zeichen_case_b_tl {}
\c_zeichen_case_c_tl {}
\c_zeichen_case_d_tl {}
}
{\PackageWarning{noname}{Falsches~Zeichen~(tl):~\l_zeichen_tl}}
}
\int_step_function:nnnN {1} {1} {\l_zeichenzahl_int} \zeichen_pruefen:n
}
\cs_new_protected:Npn \test_mit_str:n #1
{
\int_set:Nn \l_zeichenzahl_int {\str_count_ignore_spaces:n {#1}}
\cs_set:Npn \zeichen_pruefen:n ##1
{
\str_set:Nx \l_zeichen_str {\str_item_ignore_spaces:nn {#1} {##1}}
\str_case:VnF {\l_zeichen_str}
{
{0} {}
{1} {}
{a} {}
{b} {}
}
{\PackageWarning{noname}{Falsches~Zeichen~(str):~\l_zeichen_str}}
}
\int_step_function:nnnN {1} {1} {\l_zeichenzahl_int} \zeichen_pruefen:n
}
\cs_generate_variant:Nn \str_case:nnF {V}
\NewDocumentCommand \Befehl {m}
{
\test_mit_tl:n {#1}
\test_mit_str:n {#1}
}
\ExplSyntaxOff
\begin{document}
\Befehl{12ab}
\Befehl{12 ab}
\end{document}
Im Log:
Package noname Warning: Falsches Zeichen (tl): 2 on input line 63.
Package noname Warning: Falsches Zeichen (str): 2 on input line 63.
Package noname Warning: Falsches Zeichen (tl): 2 on input line 65.
Package noname Warning: Falsches Zeichen (str): 2 on input line 65.
----------
Das ganze geht aber auch erheblich leichter:
\documentclass{article}
\usepackage{expl3,xparse}
\ExplSyntaxOn
\tl_new:N \l_check_input_allowed_tl
\tl_set:Nn \l_check_input_allowed_tl {01ab}
\seq_new:N \l_check_input_wrong_seq
\cs_new_protected:Npn \check_input:n #1
{
\seq_clear:N \l_check_input_wrong_seq
\tl_map_inline:nn {#1}
{
\tl_if_in:NnF \l_check_input_allowed_tl {##1}
{ \PackageWarning {noname} {Falsche~ Zeichen:~ `##1'} }
}
}
\NewDocumentCommand \checkinput {m}
{ \check_input:n {#1} }
\ExplSyntaxOff
\begin{document}
\checkinput{}
\checkinput{foo}
\checkinput{ab10}
\checkinput{a1b0}
\checkinput{a1c0}
\checkinput{ab 10}
\end{document}
Im Log:
Package noname Warning: Falsche Zeichen: `f' on input line 26.
Package noname Warning: Falsche Zeichen: `o' on input line 26.
Package noname Warning: Falsche Zeichen: `o' on input line 26.
Package noname Warning: Falsche Zeichen: `c' on input line 29.