Das Dollarzeichen hat aus sich heraus keine Macht, Mathematik zu eröffnen. Übergibt man diese Aufgabe an !, wird !x_1! als Formel gesetzt – ohne eine einzige Fehlermeldung –, denn über die Rolle entscheidet nicht das Zeichen, sondern sein Kategoriecode (catcode), jene Zahl von 0 bis 15, die TeX jedem gelesenen Zeichen zuordnet. Diese eine Zahl erklärt, warum die internen Befehle von LaTeX voller @ stecken, was \makeatletter tatsächlich tut und warum % den Rest einer Zeile kommentarlos verschluckt. Diese Seite geht alle sechzehn Codes durch, dazu das Primitiv \catcode, das sie umschreibt, die einzeilige Wahrheit hinter \makeatletter und die Werkzeuge \string, \meaning und \detokenize, mit denen sich jedes Token untersuchen lässt.
Die sechzehn Kategoriecodes und welches Zeichen welchen bekommt
Es gibt sechzehn Kategoriecodes, nummeriert von 0 bis 15. TeX nutzt sie in dem Schritt, in dem die Quelle in Tokens zerlegt wird – lexikalische Analyse, lange vor jedem Satz. Ob ein Zeichen einen Befehl beginnt, eine Gruppe öffnet oder bloß Druckerschwärze ist, entscheidet allein diese Zahl. Im Alltag fallen auf: 0 (Befehlsbeginn), 1 und 2 (Gruppierung), 3 (Mathematik), 4 (Zellentrenner in Tabellen), 5 (das Zeilenende, das Absätze bildet), 6 (Argumentmarke eines Makros), 7 und 8 (Hoch- und Tiefstellung), das Trio 10, 11, 12 für den laufenden Text sowie 14 (Kommentare).
| catcode | Rolle | Standardzeichen (in einem LaTeX-Dokument) |
|---|---|---|
0 | Escape – beginnt einen Befehl | Backslash \ |
1 | Gruppenbeginn | Linke geschweifte Klammer { |
2 | Gruppenende | Rechte geschweifte Klammer } |
3 | Mathematikwechsel | Dollarzeichen $ |
4 | Ausrichtungstabulator – Zellentrenner | Kaufmanns-Und & |
5 | Zeilenende – eine Leerzeile erzeugt einen Absatz | Wagenrücklauf (Zeichen 13) |
6 | Parameter – Makroargument | Doppelkreuz # |
7 | Hochstellung | Zirkumflex ^ |
8 | Tiefstellung | Unterstrich _ |
9 | Ignoriert – wird übersprungen | in LaTeX standardmäßig gar kein Zeichen |
10 | Leerzeichen | Leerzeichen und Tabulator (Zeichen 9) |
11 | Buchstabe – darf Befehlsnamen bilden | a–z, A–Z |
12 | Sonstiges – bloße Druckerschwärze | Ziffern, Satzzeichen, @ und alles Übrige |
13 | Aktiv – das Zeichen ist selbst ein Befehl | Tilde ~ und Steuerzeichen 1–31 |
14 | Kommentar – verwirft den Rest der Zeile | Prozentzeichen % |
15 | Ungültig – löst einen Fehler aus | Nullzeichen (0) und Löschzeichen (127) |
Die Zeile für 9 verdient Aufmerksamkeit. Die meisten Darstellungen schreiben „catcode 9 ist das Nullzeichen“, doch das beschreibt nacktes TeX (IniTeX). Durchsucht man unter TeX Live 2024 in LaTeX alle 256 Zeichen, hat kein einziges den catcode 9: latex.ltx verschiebt das Nullzeichen auf 15 (ungültig) und lässt 9 leer – als einzige der sechzehn Kategorien ohne Standardbewohner. Sie tut nichts, bis ihr selbst ein Zeichen zugewiesen wird. Genau das macht das Paket listings: Beim Einlesen der eigenen Quelle senkt es Tabulator, Seitenvorschub und Wagenrücklauf auf catcode 9, damit sie übersprungen werden. Am anderen Ende sind die Steuerzeichen 1–31 aktiv (13) – die Maschinerie des modernen LaTeX für UTF-8-Eingaben.
Die Rolle eines Zeichens mit \catcode umschreiben
Die Zuordnung von Zeichen zu Kategorien ist nicht fest: Das Primitiv \catcode schreibt sie um. Die Form lautet „\catcode, dann ein Backtick, dann das Zielzeichen, dann =, dann die neue Zahl“; \the\catcode mit demselben Backtick und Zeichen liest den aktuellen Wert zurück. Der folgende Code übergibt die Aufgabe des Mathematikwechsels (catcode 3) an !. Es gibt null Fehler, und sowohl !x_1! als auch !\frac{a}{b}! werden korrekt als Formeln gesetzt. $ eröffnet Mathematik nicht „weil es ein Dollarzeichen ist“, sondern „weil es catcode 3 hat“ – und der Reiz dieser Maschinerie liegt darin, dass eine Zeile das beweist.
\documentclass{article}
\begin{document}
\catcode`\!=3 % hand the math-shift job to "!"
!x_1! and !\frac{a}{b}! % typeset as mathematics, zero errors
\catcode`\!=12 % give it back to "other"
Back to normal!
% read a catcode back
\the\catcode`\$ % 3
\the\catcode`\% % 14
\the\catcode`A % 11
\end{document}Es ist allerdings ein Werkzeug, das zu gut funktioniert. Kategorien werden bei der Tokenisierung eingefroren; ab dem Moment der Umschreibung ändert sich also die Bedeutung von allem Folgenden, und wer das Zurücksetzen vergisst, zerbricht etwas völlig Unbeteiligtes. Zudem wird die Änderung beim Schließen einer Gruppe ({ } oder Umgebung) rückgängig gemacht, was eigene Verwirrung stiftet: „in der Präambel funktionierte es, im Text nicht“. Ein rohes \catcode ist das letzte Mittel für Fälle, die in keine feste Hülle wie das unten behandelte \makeatletter passen. Pakete tun das freilich routinemäßig: Lädt man babel mit der deutschen Option, wird " catcode 13 (aktiv), sodass "a ein ä ergibt und ein Anführungszeichen samt Backtick ein „. Deshalb verhalten sich Anführungszeichen in einem deutschen Dokument plötzlich anders.
Warum % eine Zeile stillschweigend frisst und & einen Fehler auslöst
Die Antwort liegt im catcode-Wert selbst. % hat catcode 14 – „verwirf alles von hier bis zum Zeilenende“ – und meldet nie, was verworfen wurde. Only 50% of the sample survived. mit The rest did not. in der nächsten Zeile ergibt daher Only 50The rest did not., bei null Fehlern und null Warnungen. Eine Zeile Text ist verschwunden, das Log bleibt vollkommen still. Das ist einer der am schwersten zu entdeckenden Fehler in LaTeX überhaupt; im laufenden Text gehört ein Prozentzeichen stets als \% geschrieben. & dagegen hat catcode 4 (Zellentrenner in Tabellen), weshalb es außerhalb einer Tabelle sofort ein ! Misplaced alignment tab character &. gibt. Und $ hat catcode 3 – bleibt es ungeschlossen, folgt ! Missing $ inserted.
% catcode 14: everything after % on this line is discarded, silently
Only 50% of the sample survived.
The rest did not.
% output: "Only 50The rest did not." -- no error, no warning
% catcode 4 outside a table:
Smith & Sons % ! Misplaced alignment tab character &.
% the fix in running text:
Only 50\% of the sample survived.Catcode 11 bestimmt, wo ein Befehlsname endet
Ein TeX-Kontrollwort – ein benannter Befehl wie \section – wird erkannt als ein Zeichen mit catcode 0 (gewöhnlich \), gefolgt von einer Folge von Zeichen mit catcode 11. Nur catcode-11-Zeichen können normalerweise einen Befehlsnamen bilden, und genau deshalb endet \section bei section: Das nächste Zeichen, ein Leerzeichen oder {, hat nicht catcode 11. Ziffern und Satzzeichen tragen catcode 12 (sonstiges), weshalb \a2 als „Befehl \a, dann Zeichen 2“ gelesen wird. Umgekehrt ergibt sich der entscheidende Schritt: Setzt man den catcode eines Zeichens auf 11, wird es in Befehlsnamen verwendbar. Genau damit beginnt die Geschichte von @ im nächsten Abschnitt. (Die Folgen auf Nutzerebene – das verschluckte Leerzeichen nach einem Kontrollwort, das undefinierte \LaTeXlogo – gehören zur Seite über die Syntaxregeln.)
Was \makeatletter ist – eine einzige Zeile Code
\makeatletter setzt nichts als den catcode von @ auf 11, und sein Gegenstück \makeatother setzt ihn nur wieder auf 12. In latex.ltx besteht jede Definition buchstäblich aus einer Zeile, und die Namen sagen es: mach @ zum Buchstaben, mach @ zum Sonstigen. Warum braucht man das? Weil Kernel und Pakete von LaTeX voller interner Befehle sind, deren Namen ein @ enthalten – \@startsection (Aufbau einer Abschnittsüberschrift), \@ifnextchar (Blick auf das nächste Token zum Verzweigen), \@maketitle (der Titelblock) und Hunderte mehr. Während \usepackage und \documentclass eine .sty- oder .cls-Datei einlesen, schalten sie @ auf catcode 11, sodass diese Namen im Paket als einzelne Befehle gelesen werden.
% the whole of \makeatletter and \makeatother, in the LaTeX kernel
\DeclareRobustCommand\makeatletter{\catcode`\@11\relax}
\DeclareRobustCommand\makeatother{\catcode`\@12\relax}
% two abbreviations you will meet in internal code (note the values)
\newdimen\p@ \p@=1pt % 1pt -- "this saves macro space and time"
\newdimen\z@ \z@=0pt % 0pt, and doubles as the integer 0In einem gewöhnlichen Dokument hat @ catcode 12 (sonstiges). Schreibt man \p@ in Text oder Präambel, liest TeX „den Befehl \p, dann das Zeichen @“ und hält mit ! Undefined control sequence. an. Die Fehlerausgabe bricht bei l.3 Value: \p ab und beginnt die nächste Zeile mit @ – man sieht buchstäblich, wo der Befehlsname abgeschnitten wurde; dieses Symptom lohnt sich zu merken. Nebenbei: \p@ ist 1pt; 0pt bedeutet \z@, und die beiden zu verwechseln ist ein häufiger Ausrutscher. Der Kommentar, der in latex.ltx bis heute steht – die Abkürzung „spare Makroplatz und Zeit“ –, ist ein Relikt knapper Speicher und läuft unverändert weiter.
! Undefined control sequence.
l.3 Value: \p
@
The control sequence at the end of the top line
of your error message was never \def'ed.Wann \makeatletter nötig ist – und wann es nicht hingehört
Die praktische Regel passt in einen Satz: Nötig ist es nur in einem .tex-Dokument – fast immer in der Präambel –, wenn ein interner Befehl mit @ im Namen geschrieben wird. Sonst nirgends. Insbesondere gehört es nicht in eine .sty- oder .cls-Datei: Beim Einlesen hat @ dort bereits catcode 11, es ist also überflüssig, und ein versehentliches \makeatother kann Nachfolgendes zerstören. Der typische Einsatz ist eine leichte Anpassung eines internen Makros der Klasse in der Präambel. Das Beispiel definiert \@maketitle, das Makro für den Titelblock, innerhalb der Hülle neu.
\documentclass{article}
\makeatletter % @ becomes a letter here
\renewcommand{\@maketitle}{% % redefine the internal title block
\begin{center}
{\LARGE\bfseries \@title}\par
\vspace{1ex}{\large \@author}\par
\end{center}%
}
\makeatother % ... and goes back to "other" here
\title{Category codes}
\author{A. Author}
\begin{document}
\maketitle
\end{document}Es gibt zwei Fallen. Die erste ist das vergessene \makeatother: Bleibt es aus, ist @ für alles Folgende ein Buchstabe und zerstört eine E-Mail-Adresse im Text oder jedes Paket, das @ besonders behandelt. Am besten mechanisch merken – mit dem einen öffnen, mit dem anderen schließen. Die zweite ist, nicht zu prüfen, ob sich das Anfassen von Internas ganz vermeiden lässt. Kann ein öffentlicher Befehl mit \renewcommand überschrieben werden, oder liefert ein passendes Paket das Gewünschte bereits, ist dieser Weg immer sicherer. Interne Befehle ändern sich mit Paketaktualisierungen ohne Ankündigung, und eine in \makeatletter geschriebene Umdefinition hört beim nächsten Release still auf zu wirken.
Warum \verb und listings an den catcodes drehen müssen
Um Quelltext exakt so zu drucken, wie er dasteht, bleibt nur ein Weg: den Sonderzeichen das Besondere nehmen. Genau das tut \verb, und die Umgebung verbatim tut dasselbe für einen ganzen Block. Es wendet \@makeother auf die elf in \dospecials aufgezählten Zeichen an – Leerzeichen, \, {, }, $, &, #, ^, _, %, ~ – und senkt sie sämtlich auf catcode 12 (sonstiges). Anschließend geht \@noligs in die Gegenrichtung und macht sechs weitere catcode 13 (aktiv): den Backtick, <, >, ,, den Apostroph und -, damit zwei Backticks nicht stillschweigend zu einem typografischen Anführungszeichen verschmelzen. \verb senkt also catcodes und hebt sie zugleich; da das Ganze in einem \bgroup steckt, wird am Ende alles automatisch zurückgesetzt.
\def\@makeother#1{\catcode`#1=12\relax}
\def\dospecials{\do\ \do\\\do\{\do\}\do\$\do\&%
\do\#\do\^\do\_\do\%\do\~}
\def\verb{\relax\ifmmode\hbox\else\leavevmode\null\fi
\bgroup % everything below is local
\verb@eol@error \let\do\@makeother \dospecials % all 11 -> catcode 12
\verbatim@font\@noligs % ` < > , ' - -> catcode 13
\language\l@nohyphenation
\@ifstar\@sverb\@verb}Daraus folgt die berühmte Einschränkung von \verb: \verb lässt sich nicht im Argument eines anderen Befehls verwenden. Das Argument wird tokenisiert, bevor das Makro überhaupt aufgerufen wird; wenn \verb einen catcode ändert, ist es längst zu spät. \footnote{code: \verb|\foo_bar|} liefert ! Undefined control sequence. (weil \foo als echter Befehl gelesen wurde), gefolgt von ! Missing $ inserted. (weil _ als Tiefstellung gelesen wurde); im Makroargument kommt zusätzlich ! Extra }, or forgotten $. hinzu. Der Ausweg ist \lstinline aus listings: \section{A \lstinline|x_1| heading} übersetzt fehlerfrei und übersteht sogar den Weg ins Inhaltsverzeichnis. Die gut vierzig catcode-Eingriffe in listings sind genau dafür da, solche Probleme abzufangen.
% BREAKS: the argument is tokenized before \verb can act
\footnote{code: \verb|\foo_bar|}
% ! Undefined control sequence. <argument> ... \verb |\foo
% ! Missing $ inserted.
% WORKS: \lstinline survives inside a moving argument
\usepackage{listings}
\section{A \lstinline|x_1| heading} % zero errors, reaches the ToCIn ein Token hineinsehen: \string, \meaning, \detokenize
Wirken Kategoriecodes noch abstrakt, hilft am schnellsten, sie anzuschauen. \meaning benennt den catcode in Worten: \meaning A liefert the letter A (catcode 11, daher „letter“), \meaning 7 liefert the character 7 (catcode 12), \meaning\bgroup liefert begin-group character {. Eine Wendung genügt, um zu wissen, was ein Zeichen gerade ist. \string zerlegt einen Befehl in seine Zeichen samt Backslash, und \detokenize verwandelt ein ganzes Argument in druckbaren Text – dass # als ## erscheint, ist die Verdopplung, die die Tokenliste originalgetreu hält. Zum Debuggen ist auch \show nützlich: Es schreibt eine Definition ins Log und hält an.
| Befehl | Wirkung | Gemessene Ausgabe unter TeX Live 2024 |
|---|---|---|
\the\catcode | Liest den catcode eines Zeichens als Zahl | \the\catcode + Backtick + $ ergibt 3 |
\meaning | Beschreibt ein Token in Worten | \meaning A ergibt the letter A; \meaning 7 ergibt the character 7 |
\string | Zerlegt einen Befehl in Zeichen, samt Backslash | \string\frac ergibt \frac |
\detokenize | Verwandelt ein ganzes Argument in druckbaren Text | \detokenize{\frac{1}{2} #1} ergibt \frac {1}{2} ##1 |
\show | Schreibt eine Definition ins Log und hält an | \show\LaTeX ergibt macro:->\protect \LaTeX |
Zum Schluss noch eine praktische Faustregel. Trifft man auf einen Fehler der Form „ausgerechnet dieses eine Zeichen spinnt“, prüft man zuerst mit \the\catcode dessen Nummer. Pakete wie babel, csquotes, listings und hyperref machen für ihre Zwecke bestimmte Zeichen aktiv oder senken sie auf catcode 12. Beschränkt sich das Symptom auf ein einzelnes Zeichen, ist in neun von zehn Fällen ein Kategoriecode die Ursache. Damit engt sich die Behebung auf drei Möglichkeiten ein: Ladereihenfolge ändern, das Zeichen meiden oder den vom Paket bereitgestellten Ausweg nutzen (\%, ein anderes Begrenzungszeichen für \verb, \lstinline).