Ein Befehlsargument, in dem nur bestimmte Zeichen (sagen wir beispielshalber 0, 1, a und b) erlaubt sein sollen, soll vor der weiteren Verarbeitung im Hinblick hierauf überprüft werden. Idee hierzu: Mit einer for-Schleife wird der gesamte Inhalt des Arguments Zeichen für Zeichen durchlaufen. Innerhalb der Schleife überprüft dann eine case-Anweisung, ob es sich um ein erlaubtes Zeichen handelt. Ansonsten wird ein Fehlercode ausgeführt (hier eine Package-Warnung).

Ich habe eine Umsetzung auf der Grundlage zweier verschiedener expl3-Datenstrukturen (token list und string) versucht, aber beides funktioniert nicht, wie es soll. Bei der String-Variante liegt dies offenbar daran, dass das zweite Argument von \str_set:Nn nicht expandiert wird, aber ich weiß nicht, was ich ändern muss.

Open in Online-Editor
\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

\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:Nn \l_zeichen_tl {\str_item_ignore_spaces:nn {#1} {##1}}
                \tl_case:NnF \l_zeichen_tl
                    {
                        0 {}
                        1 {}
                        a {}
                        b {}
                    }
                    {\PackageWarning{noname}{Falsches~Zeichen:~\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:Nn \l_zeichen_str {\str_item_ignore_spaces:nn {#1} {##1}}
                \str_case:nnF {\l_zeichen_str}
                    {
                        {0} {}
                        {1} {}
                        {a} {}
                        {b} {}
                    }
                    {\PackageWarning{noname}{Falsches~Zeichen:~\l_zeichen_str}}
            }
        \int_step_function:nnnN {1} {1} {\l_zeichenzahl_int} \zeichen_pruefen:n
    }

\NewDocumentCommand \Befehl {m}
    {
        \test_mit_tl:n {#1}

        \test_mit_str:n {#1}
    }

\ExplSyntaxOff

\begin{document}
\Befehl{12ab}
\end{document}

gefragt 23 Mai '16, 17:41

Cletus's gravatar image

Cletus
1.6k45566
Akzeptiert-Rate: 75%

bearbeitet 23 Mai '16, 17:43

Sehe ich das richtig, dass Leerzeichen ignoriert werden sollen? Was ist mit komplett leerem Input?

(24 Mai '16, 09:54) cgnieder

Ja, Leerzeichen stören in diesem Fall nicht und sollen ignoriert werden. Eine leere Eingabe ist dagegen nicht erwünscht/sinnvoll.

(24 Mai '16, 12:54) Cletus

Du hast in beiden Definitionen kleine aber wesentliche Fehler. In der Tokenlist-Definition hast Du folgende Zeile:

Öffne in Overleaf
\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.

Öffne in Overleaf
> \l_zeichen_tl=\str_item_ignore_spaces:nn {12ab}{1}

Ändere die Zeile in

Öffne in Overleaf
\tl_set:Nx \l_zeichen_tl {\str_item_ignore_spaces:nn {#1} {##1}}

und daraus wird

Öffne in Overleaf
> \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

Öffne in Overleaf
\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:

Öffne in Overleaf
\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

Öffne in Overleaf
\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:

Öffne in Overleaf
\tl_set:Nx \l_zeichen_tl {\tl_item:nn {#1} {##1}}

Und jetzt klappt der Test.


In der String-Variante hast Du ähnliche Fehler:

Öffne in Overleaf
\str_set:Nn \l_zeichen_str {\str_item_ignore_spaces:nn {#1} {##1}}

Ändere das in

Öffne in Overleaf
\str_set:Nx \l_zeichen_str {\str_item_ignore_spaces:nn {#1} {##1}}

Außerdem hast Du

Öffne in Overleaf
\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

Öffne in Overleaf
\str_case:VnF \l_zeichen_str ...

die Du am besten mit

Öffne in Overleaf
\cs_generate_variant:Nn \str_case:nnF {V}

bereitstellst.


Mit diesen Korrekturen funktioniert Dein Code:

Öffne in Overleaf
\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:

Öffne in Overleaf
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:

Öffne in Overleaf
\documentclass{article}
\usepackage{expl3,xparse}

\ExplSyntaxOn
\tl_new:N \l_check_input_allowed_tl
\tl_set:Nn \l_check_input_allowed_tl {01ab}

\cs_new_protected:Npn \check_input:n #1
  {
    \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:

Öffne in Overleaf
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.
Permanenter link

beantwortet 24 Mai '16, 13:20

cgnieder's gravatar image

cgnieder
22.1k243463
Akzeptiert-Rate: 60%

bearbeitet 09 Apr '17, 15:32

Ich würde das mit einer clist machen. Testet auch auf Leerzeichen und nutzt das l3msg-Interface für Warnungen.

Open in Online-Editor
\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\clist_new:N \l_allowed_chars_clist
\clist_set:Nn \l_allowed_chars_clist { 0, 1, a, b }

\msg_new:nnn { noname } { erroneous-input } { Erroneous~character~`#1' }

\cs_new_protected:Npn \test_mit_tl:n #1
 {
  % Check for spaces
  \seq_set_split:Nnn \l_tmpa_seq { ~ } { #1 }
  \int_compare:nT { \seq_count:N \l_tmpa_seq > 1 }
   { \msg_warning:nnn { noname } { erroneous-input } { ~ } }

  % Check for invalid tokens
  \tl_map_inline:nn { #1 }
   {
    \clist_if_in:NnF \l_allowed_chars_clist { ##1 }
     { \msg_warning:nnn { noname } { erroneous-input } { ##1 } }
   }
 }

\NewDocumentCommand \Befehl {m}
 {
  \test_mit_tl:n {#1}
 }

\ExplSyntaxOff

\begin{document}
\Befehl{12 ab}
\end{document}
Permanenter link

beantwortet 23 Mai '16, 18:54

Henri's gravatar image

Henri
15.7k133943
Akzeptiert-Rate: 46%

bearbeitet 23 Mai '16, 22:33

Der zweite \seq_set_split-Befehl scheint mir da nicht mehr hinzupassen.

(23 Mai '16, 22:32) Cletus

@Cletus In der Tat. Das war noch ein Relikt aus früheren Versuchen.

(23 Mai '16, 22:34) Henri

Etwas unschön ist es, dass die Warnung zweimal ausgegeben wird (jeweils mit einem Fragezeichen anstelle des eigentlichen Zeichens), falls das falsche Zeichen ein Umlaut ist. Der Hintergrund ist mir klar: In UTF-8 werden Umlaute anders als gewöhnliche lateinische Buchstaben durch zwei Byte codiert. Aber kann eine moderne, in Unicode-Zeiten entstandene Sprache wie expl3 so etwas nicht angemessener behandeln?

(26 Mai '16, 00:26) Cletus

@Cletus Unicode-Engine (xelatex, lualatex) benutzen. Damit bekomme ich Erroneous character `ö'. In den alten 8-bit Engines kann man Unicode-Zeichen nicht anders kodieren als mit zwei Byte.

(26 Mai '16, 00:29) Henri
Deine Antwort
Vorschau umschalten

Folgen dieser Frage

Per E-Mail:

Wenn sie sich anmelden, kommen Sie für alle Updates hier in Frage

Per RSS:

Antworten

Antworten und Kommentare

Markdown-Grundlagen

  • *kursiv* oder _kursiv_
  • **Fett** oder __Fett__
  • Link:[Text](http://url.com/ "Titel")
  • Bild?![alt Text](/path/img.jpg "Titel")
  • nummerierte Liste: 1. Foo 2. Bar
  • zum Hinzufügen ein Zeilenumbruchs fügen Sie einfach zwei Leerzeichen an die Stelle an der die neue Linie sein soll.
  • grundlegende HTML-Tags werden ebenfalls unterstützt

Frage-Themen:

×13
×5

gestellte Frage: 23 Mai '16, 17:41

Frage wurde gesehen: 6,123 Mal

zuletzt geändert: 09 Apr '17, 15:32