Parsen bei \newcommand unterdrücken?

Fragen und Probleme, die nicht den obigen Kategorien zugeordnet werden können


Rolli
Forum-Guru
Forum-Guru
Beiträge: 345
Registriert: Mi 15. Feb 2017, 08:50
Wohnort: Mittelfranken

Parsen bei \newcommand unterdrücken?

Beitrag von Rolli »

Hallo allerseits,

ich habe mal wieder ein (vermutlich recht kleines) Problem, das ich aber nicht alleine hin bekomme - mein altes Thema: Ich suche schon seit längerer Zeit, aber vermutlich mit dem falschen Suchbegriff (schäm).

Die Frage ist eigentlich recht simpel: Was muss ich \newcommand mitgeben bzw. ändern, damit der LaTeX-Compiler die übergebenen Argumente nicht parst, sondern 1:1 weiter gibt? Und dass mein selbst definiertes Makro das ebenfalls fehlerfrei verarbeiten kann? Sozusagen etwas in der Art, wie es verbatim macht.

Im Minimalbeispiel möchte ich, dass der zweite \myCommand-Aufruf einfach Hello_world! setzt, ohne das Gemeckere, dass eine Mathematik-Umgebung fehle.

Code: Alles auswählen

\documentclass{scrartcl}

\begin{document}
\newcommand{\myCommand}[1]{#1}

\myCommand{Hello, world!\par} % funktioniert natürlich
\myCommand{Hello_world!\par}  % bringt Fehler
\end{document}
Danke für Eure Gedankenanstöße!

Gruß vom Rolli[/m]

Gast

Beitrag von Gast »

Das Zauberwort heißt \catcode. Der Category-Code eines Zeichens bestimmt, was für ein Token beim Einlesen des Quellcodes gebildet wird. Das bedeutet, sobald ein Zeichen gelesen ist, ist es zu spät dafür.

Genau damit arbeitet \verb. Deshalb ist \verb auch nicht so definiert, dass es selbst ein Argument einliest. Im LaTeX-Kern wirst Du also weder etwas wie \newcommand*{\verb}[1] noch etwas wie \def\verb#1 finden. Stattdessen beginnt es mit \def\verb{. Dann wird als zentrales Element eine Gruppe geöffnet, Category-Codes geändert (u. a. über \dospecials) und weitere Umdefinierungen vorgenommen und erst dann wird das Argument gelesen, wobei das im Fall on \verb auch noch ziemlich ungewöhnlich erfolgt, weil \verb eine sehr spezielle Form eines Argument verarbeitet. Am Ende muss natürlich auch noch die Gruppe wieder geschlossen und damit die Änderungen rückgängig gemacht werden.

Es wird also nicht etwa das Parsen unterdrückt, sondern der Parser umprogrammiert, bevor das Argument gelesen wird.

Es ist zu beachten, dass einige Zeichen im normalen Encoding von Fonts gar nicht in der gewünschten Form enthalten sind. Beispielsweise würde ein \ im normalen Textfont nicht funktionieren, weil auf der entsprechenden Code-Position ein anderes Zeichen liegt. Deshalb ergeben beispielsweise \string\test und \texttt{\string\test} unterschiedliche Ergebnisse.

In deinem Fall kommt erschwerend hinzu, dass Du offenbar gar nicht das Verhalten von \verb willst. Das würde nämlich \par als Text ausgeben und keinen Absatz erzeugen. Damit käme eher die Verwendung der alltt-Umgebung des Pakets alltt in Frage, beispielsweise in der Form:

Code: Alles auswählen

\documentclass{scrartcl}
\usepackage{alltt}

\makeatletter
\newcommand{\myCommand}{%
  \begin{alltt}\@myCommand
}
\newcommand{\@myCommand}[1]{#1\end{alltt}}
\makeatother

\begin{document}
\myCommand{Hello, world!\par} % funktioniert natürlich
\myCommand{Hello_world!\par}  % funktioniert natürlich auch
\end{document}
Die Gruppe (in Form der Umgebung alltt wird hier also geöffnet und damit Category-Codes geändert, bevor \@myCommand das Argument liest.

An dem Beispiel kann man auch zeigen, dass das ganze von \ttfamily abhängt:

Code: Alles auswählen

\documentclass{scrartcl}
\usepackage{alltt}

\makeatletter
\newcommand{\myCommand}{%
  \begin{alltt}\normalfont\@myCommand
}
\newcommand{\@myCommand}[1]{#1\end{alltt}}
\makeatother

\begin{document}
\myCommand{Hello, world!\par} % funktioniert natürlich
\myCommand{Hello_world!\par}  % funktioniert natürlich auch
\end{document}
bringt nicht das gewünschte Ergebnis, weil normalerweise an der Code-Position von _ ein anderes Zeichen liegt. Man müsste also diverse Zeichen noch explizit umdefinieren, beispielsweise:

Code: Alles auswählen

\documentclass{scrartcl}
\usepackage{alltt}

\makeatletter
\newcommand{\myCommand}{%
  \begin{alltt}\normalfont\myCommand@extraspecials\@myCommand
}
\newcommand{\@myCommand}[1]{#1\end{alltt}}
\begingroup
\catcode`\_=\active
\gdef\myCommand@extraspecials{%
  \catcode`\_=\active
  \let_=\_
}
\makeatother

\begin{document}
\myCommand{Hello, world!\par} % funktioniert natürlich
\myCommand{Hello_world!\par}  % funktioniert natürlich auch
\end{document}
Du siehst, das ganze kann je nachdem, was man braucht und will, recht aufwändig werden. Dagegen ist es recht einfach, ein zweites \verb zu definieren, also eine Anweisung, die sich tatsächlich wie \verb verhällt:

Code: Alles auswählen

\documentclass{scrartcl}
\newcommand*{\myCommand}{}
\let\myCommand\verb

\begin{document}
\myCommand*+Hello, world!\par+ % funktioniert natürlich
\myCommand+Hello_world!\par+  % funktioniert natürlich auch
\end{document}
Wenn Du Dich tiefer mit Category-Codes und der Funktion des Scanners und des Parsers von TeX beschäftigen willst, sei auf TeX by Topic verwiesen (ist seit einigen Jahren frei erhältlich und zumindest bei TeX Live sogar dabei). Daneben natürlich auf das Standardwerk zu TeX: The TeXbook. Letzteres ist wirklich schwere Kost. Wer das verstanden hat, hat dann aber auch verstanden, wie TeX funktioniert und warum einige scheinbar einfache Dinge nicht ganz einfach zu lösen sind.

Benutzeravatar
u_fischer
Forum-Meister
Forum-Meister
Beiträge: 4014
Registriert: Do 22. Nov 2012, 11:09
Kontaktdaten:

Beitrag von u_fischer »

Nun, die einfache Variante wäre \detokenize. Aber ich habe den Verdacht, dass du nicht wirklich willst, dass dein Befehl sich verbatim verhält, sondern dass du nur bestimmte Eingaben (wie _) anders behandeln willst.

Code: Alles auswählen

\documentclass{scrartcl}
\usepackage[T1]{fontenc} %wichtig für korrekte Ausgabe
\begin{document}
\newcommand{\myCommand}[1]{\detokenize{#1}}

\verb+Hello_world!\par+

\myCommand{Hello, world!\par} % funktioniert natürlich
\myCommand{Hello_world!\par}  % 
\end{document} 

Gast

Beitrag von Gast »

u_fischer hat geschrieben:Nun, die einfache Variante wäre \detokenize.
Wobei \detokenize Leerzeichen nach vermeintlichen Befehlen einfügt:

Code: Alles auswählen

\documentclass{scrartcl}
\usepackage[T1]{fontenc} %wichtig für korrekte Ausgabe
\begin{document}
\newcommand{\myCommand}[1]{\detokenize{#1}}

\verb+Hello_world!\par\par+

\myCommand{Hello, world!\par\par} % funktioniert natürlich
\myCommand{Hello_world!\par\par}  %
\end{document}
und die Ausgabe eben wieder vom Font-Encoding abhängt. Ob das eine Rolle spielt, hängt vom tatsächlichen Einsatzzweck ab.

Gast

Beitrag von Gast »

Anonymous hat geschrieben:Ob das eine Rolle spielt, hängt vom tatsächlichen Einsatzzweck ab.
Verwendet man beispielsweise Umlaute mit pdflatex, so spielt das recht schnell eine Rolle:

Code: Alles auswählen

\documentclass{scrartcl}
\usepackage[T1]{fontenc} %wichtig für korrekte Ausgabe
\begin{document}
\newcommand{\myCommand}[1]{\detokenize{#1}}

\verb+Märchen+ vs. \myCommand{Märchen}
\end{document}
Mit lualatex ist zumindest das hingegen kein Problem.

Code: Alles auswählen

\documentclass{scrartcl}
\usepackage{fontenc}
\begin{document}
\newcommand{\myCommand}[1]{\detokenize{#1}}

\verb+Märchen+ vs. \myCommand{Märchen}
\end{document}
Die Ausgabe von \detokenize ist also nicht nur vom Font-Encoding, sondern auch von der Eingabecodierung abhängig und so indirekt auch von der Engine.

Rolli
Forum-Guru
Forum-Guru
Beiträge: 345
Registriert: Mi 15. Feb 2017, 08:50
Wohnort: Mittelfranken

Konkrete Anwendung

Beitrag von Rolli »

Ohje, was habe ich da wieder angerichtet! Ich dachte, es sei ganz einfach ...
Zunächst Euch vielen Dank für die Informationen - wieder was gelernt.
So richtig weiter komme ich damit aber nicht. Ich möchte Folgendes tun:
Im Dateisystem habe ich hierarchisch verschiedene Dokumente, und ich möchte eine Übersicht erstellen, welche Unterverzeichnisse und Dokumente vorhanden sind. Das Ganze soll anklickbar sein - so dass sich - je nach Eintragtyp - automatisch ein Explorerfenster (bei Verzeichnissen), oder ein PDF-Betrachter usw. öffnet.
Das funktioniert auch alles ganz gut - bis auf den Umstand, dass es leider Dateienamen gibt, in denen ein Underscore _ ist; möglicherweise auch ein Prozent %. Unten ein konkretes Beispiel.

Mit alltt geht das leider nicht, da ich in meinem \aEntry das \item drin habe.
Ich fürchte, da muss ich mich mit \catcode auseinandersetzen - oder hat jemand eine einfachere Idee?

Gruß vom Rolli

Code: Alles auswählen

\documentclass{scrartcl}
\usepackage{xcolor}
\usepackage{csquotes}
\usepackage{hyperref}

\begin{document}

\newcommand\aEntry[4]{% #1 FILE oder DIR    #2 Farbe    #3 Link    #4 Beschreibungstext
   \item [\color{#2}#1] \href[pdfnewwindow]{run:#3}{\color{#2}\textquote{#3}}{\color{#2}: #4}
}

\begin{description}
   \aEntry{FILE}{black}{Uebersicht.pdf}{Diese Datei}
   \aEntry{DIR~}{black}{Beispiele}{In diesem Unterverzeichnis sind die Beispiele}
   \begin{description}
      \aEntry{FILE}{red}{Beispiele/Beispiel1.tex}{Hier ist das erste Beispiel}
      \aEntry{FILE}{black}{Beispiele/Beispiel2.tex}{Hier ist das zweite Beispiel}
      \aEntry{DIR~}{black}{Beispiele/Fortgeschritten}{In diesem Unterverzeichnis sind kompliziertere Beispiele}
      \begin{description}
         \aEntry{FILE}{red}{Beispiele/Fortgeschritten/Beispielx.tex}{Hier ist ein kompliziertes Beispiel}
      \end{description}
   \end{description}
\end{description}

\end{document}

Antworten