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, 11:41

Cletus's gravatar image

Cletus
995126
Akzeptiert: 71%

bearbeitet 23 Mai, 11:43

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

(24 Mai, 03:54) Clemens

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

(24 Mai, 06:54) Cletus

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

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

Open in Online-Editor
> \l_zeichen_tl=\str_item_ignore_spaces:nn {12ab}{1}

Ändere die Zeile in

Open in Online-Editor
\tl_set:Nx \l_zeichen_tl {\str_item_ignore_spaces:nn {#1} {##1}}

und daraus wird

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

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

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

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

Open in Online-Editor
\tl_set:Nx \l_zeichen_tl {\tl_item:nn {#1} {##1}}

Und jetzt klappt der Test.


In der String-Variante hast Du ähnliche Fehler:

Open in Online-Editor
\str_set:Nn \l_zeichen_str {\str_item_ignore_spaces:nn {#1} {##1}}

Ändere das in

Open in Online-Editor
\str_set:Nx \l_zeichen_str {\str_item_ignore_spaces:nn {#1} {##1}}

Außerdem hast Du

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

Open in Online-Editor
\str_case:VnF \l_zeichen_str ...

die Du am besten mit

Open in Online-Editor
\cs_generate_variant:Nn \str_case:nnF {V}

bereitstellst.


Mit diesen Korrekturen funktioniert Dein Code:

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

\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:

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

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

Open in Online-Editor
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, 07:20

Clemens's gravatar image

Clemens
19.0k112960

bearbeitet 26 Mai, 06:12

Danke für die Mühe, da habe ich wieder viel gelernt. Welchen Zweck hat die Sequenz \l_check_input_wrong_seq in deinem letzten Codebeispiel? Könnte man die Warnungen nicht genauso gut direkt in der \tl_map_inline-Schleife ausgeben (vgl. Henris Vorschlag)?

(24 Mai, 17:33) Cletus

@Cletus ja könnte man. Dann wäre es noch kürzer. Wenn man mit dem falschen Input noch was machen will, ist die Sequenz vielleicht ganz nützlich, in dem Beispiel aber eher überflüssig.

(24 Mai, 17:35) Clemens

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, 12:54

Henri's gravatar image

Henri
10.0k31730

bearbeitet 23 Mai, 16:33

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

(23 Mai, 16:32) Cletus

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

(23 Mai, 16: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?

(25 Mai, 18: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.

(25 Mai, 18:29) Henri
Deine Antwort auf die Frage (nicht auf andere Antworten)
Knebel-Vorschau

Folge dieser Frage

Per E-Mail:

Wenn Du Dich anmeldest, kannst Du Updates hier abonnieren

Per RSS:

Antworten

Antworten und Kommentare

Aktuelle Buch-Infos

LaTeX Cookbook

LaTeX Beginners Guide

Limitierter Rabatt ebook
50% Coupon code tDRet6Y

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üge einfach zwei Leerzeichen an die Stelle ein, an der die neue Zeile sein soll.
  • grundlegende HTML-Tags werden ebenfalls unterstützt

Zugeordnete Themen:

×8
×4

Frage gestellt: 23 Mai, 11:41

Frage wurde angeschaut: 441 Mal

Zuletzt aktualisiert: 26 Mai, 06:12