Spaltenspezifikation, Ausrichtung und Breite

Das {lcr} in \begin{tabular}{lcr} ist eine der wenigen Stellen in LaTeX, an denen man ein Programm statt Text schreibt. Über Spaltenzahl und Ausrichtung hinaus lässt sich hier anweisen, vor und nach jeder Zelle eine Deklaration einzufügen, den Abstand zwischen Spalten durch etwas anderes zu ersetzen oder eine Gruppe von Spalten zu wiederholen. Und das Vokabular dieser Spaltendeklaration entstammt keinem einheitlichen Entwurf, sondern wuchs durch Schenkungen: \newcolumntype stammte ursprünglich aus einem eigenen Paket von David Carlisle, newarray.sty, und das Handbuch zu array hält fest, „David kindly agreed on the inclusion“. Ausgehend von l, c, r und p{width} verfolgt diese Seite die Sprache über m{width} und b{width}, >{…} und <{…}, @{…} und !{…}, *{n}{…}, w und W bis zur X-Spalte von tabularx, mit der eine Tabelle eine exakte Breite trifft.

Welche Spaltentypen welches Paket brauchen

Reines LaTeX bietet nur l, c, r, p{width}, |, @{…} und *{n}{…}. Alles Weitere – m{width}, b{width}, >{…}, <{…}, !{…}, w und W sowie \newcolumntype – stammt aus dem Paket array (in TeX Live 2024 liegt v2.5g vom 16. Oktober 2023 von Frank Mittelbach bei). Die X-Spalte kommt von tabularx (v2.11c vom 8. Juli 2023 von David Carlisle), das seinerseits array voraussetzt. Da booktabs und tabularx array intern laden, ist es oft schon aktiv; ein ausdrückliches \usepackage{array} ist dennoch sicherer.

Welche Variante im eigenen Dokument läuft, klärt ein einziger Schritt: absichtlich einen Spaltenbuchstaben schreiben, den es nicht gibt. Reines LaTeX antwortet ! LaTeX Error: Illegal character in array arg., während die Beschwerde mit geladenem array aus dem Paket kommt: ! Package array Error: Illegal pream-token. Wer also m{width} geschrieben hat und „Illegal character“ liest, dem sagt die Meldung in Wahrheit, dass array nicht geladen wurde.

Absatzspalten p, m, b – und die festbreiten w und W

Es gibt drei umbrechende Absatzspalten, die sich nur in der vertikalen Position der Zelle unterscheiden. In den Worten des array-Handbuchs ist p{width} äquivalent zu \parbox[t]{width} und damit oben ausgerichtet; m{width} setzt den Inhalt in die vertikale Mitte der Zeile; und b{width} „coincides with \parbox[b]“, ist also unten ausgerichtet. Der Unterschied zählt, wenn eine Zeile eine hohe Zelle – eine mehrzeilig umbrochene p-Spalte oder ein großes Bild – mit einzeiligen Zellen mischt. Beim Standard-p klebt die kurze Nachbarzelle am oberen Rand und liegt nicht auf Höhe der Mitte der hohen Zelle; m{width} bringt beide auf eine Linie.

Wer eine feste Breite ohne Umbruch braucht, findet in array w{align}{width} und W{align}{width}. Beide arbeiten im Wesentlichen wie \makebox[width][align]{cell}: Sie verhalten sich wie l, c oder r und legen die Spaltenbreite zugleich fest. Der Unterschied liegt im Fehlerfall – w überdruckt stillschweigend, wenn der Inhalt breiter ist als die Breite, während W in derselben Lage eine Overfull-Box-Warnung ausgibt. Zu beachten ist allerdings, dass W bei Überlauf stets nach rechts hinausragt, die Ausrichtung also anders wirkt. Lautet die Anforderung „feste Breite, aber Überlauf melden“, ist W die richtige Wahl.

TypVertikale Position / BreiteUmbruch?Bereitgestellt durch
p{wd}Oben; äquivalent zu \parbox[t]{wd}JaStandard
m{wd}Vertikal in der Zeile zentriertJaarray
b{wd}Unten; deckt sich mit \parbox[b]{wd}Jaarray
w{l}{wd}Wie \makebox[wd][l]; überdruckt stillschweigendNeinarray
W{l}{wd}Wie w, warnt aber bei Überlauf; Überlauf nach rechtsNeinarray
Xp-Spalte mit berechneter Breite für eine ZielbreiteJatabularx

Zwei kleine Vorgaben lohnen bei Absatzspalten die Erinnerung. Erstens ist innerhalb von p, m und b der Absatzeinzug \parindent gleich 0; zurückholen lässt er sich etwa mit >{\setlength{\parindent}{1em}}p{5cm}. Zweitens ergänzt array eine Länge namens \extrarowheight. Ein positiver Wert vergrößert nur die Höhe jeder Zeile und lässt die Tiefe unverändert – das direkteste Rezept gegen \hline-Linien, die die Oberkanten von Großbuchstaben zu berühren scheinen. Das array-Handbuch vermerkt, dass es für die dort abgedruckte Tabelle 1pt davon gesetzt hat.

Vor und nach jeder Zelle einfügen — >{…} und <{…}

Das Kernstück des Pakets array sind >{decl} und <{decl}. >{decl} fügt decl unmittelbar vor dem Inhalt jeder Zelle dieser Spalte ein, <{decl} unmittelbar danach. Mit >{\bfseries}l erscheint jede Zelle einer linksbündigen Spalte fett, ohne dass pro Zelle etwas getippt werden muss. >{\itshape}c ergibt zentrierte Kursive, >{\color{red}}r rechtsbündiges Rot. Für Absatzspalten lassen sich dort auch Ausrichtungsdeklarationen unterbringen: >{\raggedright}p{4cm} erzeugt eine Spalte, die „umbricht, aber nicht im Blocksatz steht“.

Es lauert allerdings eine ernste Falle. \raggedright, \raggedleft und \centering definieren intern alle den Zeilenabschluss \\ um. Steht eine Spalte, die einen davon in >{…} trägt, als letzte Spalte der Tabelle, beendet \\ keine Zeilen mehr, das & der nächsten Zeile läuft über, und es erscheint ! Extra alignment tab has been changed to \cr.. Die Ursache liegt in der Spaltendeklaration, die Meldung zeigt jedoch auf eine Zeile im Rumpf – deshalb erkennt das beim ersten Mal niemand. Die Abhilfe ist, am Ende der Deklaration arrays \arraybackslash anzuhängen: >{\raggedright\arraybackslash}p{4cm} stellt den Zeilenabschluss wieder her. Bei einer Spalte, die nicht die letzte ist, schadet es nicht, also schreibt man es besser immer.

Die Herkunft von \arraybackslash hat eine Wendung. Eingeführt wurde es von tabularx, und zwar genau zur Behebung dieses Konflikts mit \raggedright. Heute trägt array.sty selbst die Definition \def\arraybackslash{\let\\\tabularnewline}, und tabularx behält nur noch eine abgesicherte Ersatzdefinition für den Fall, dass es sonst niemand bereitstellt. Das Werkzeug wurde also in einem Paket geboren und wohnt heute in einem anderen.

latex
\usepackage{array}
% ...
% bold labels; a ragged-right wrapping column; a whole column in math mode
\begin{tabular}{ >{\bfseries}l >{\raggedright\arraybackslash}p{5cm} >{$}c<{$} }
  \hline
  Symbol & Meaning & \multicolumn{1}{c}{Value} \\
  \hline
  c & Speed of light in vacuum, a defining constant. & 2.998\times10^{8} \\
  g & Standard gravitational acceleration near the surface. & 9.807 \\
  \hline
\end{tabular}

Das >{$}c<{$} in der dritten Spalte ist genau der ursprüngliche Zweck von > und <. Jede Zelle bekommt vorn und hinten ein $, sodass die ganze Spalte als Inline-Mathematik gesetzt wird und man in zahlen- oder symbolreichen Spalten nie wieder $…$ tippt. Dasselbe in einer array-Umgebung, deren Zellen ohnehin im Mathematikmodus stehen, lässt die beiden $ sich aufheben und setzt nur diese Spalte in den Textmodus. Die Kopfzeile verlangt allerdings Aufmerksamkeit: Ein schlicht geschriebenes Value wird als Formel gesetzt und erscheint als locker gesperrte Kursive. Wie im Beispiel oben holt \multicolumn{1}{c}{Value} diese eine Zelle in den Textmodus zurück.

Zu merken ist außerdem die Einschränkung, dass in >{…} und <{…} nur eine Deklaration stehen darf. Formen ohne nachfolgendes Argument wie \bfseries oder \itshape sind unproblematisch, aber man darf nicht mit einem Befehl enden, der ein Argument nimmt. >{\textbf} beginnt, den Zellinhalt als Argument zu lesen, und scheitert auf die unangenehmste Weise: kein Fehler, und nichts wird fett. Das array-Handbuch sagt es ausdrücklich – >{\textbf} „would not make the whole column bold nor would it make the first character bold“. Für Fettdruck nimmt man die Deklarationsform \bfseries.

An der Spaltengrenze — @{…} und !{…}

Zwischen zwei Spalten fügt LaTeX standardmäßig \tabcolsep Abstand ein – 6pt auf jeder Seite, insgesamt also 12pt. Steht @{decl} an dieser Stelle der Deklaration, so „suppresses inter-column space and inserts decl. instead“, wie es im array-Handbuch heißt: Der Zwischenraum verschwindet, und an seine Stelle tritt das Geschriebene. Zwei Hauptanwendungen gibt es. Erstens äußeres Tabellenpolster entfernen: @{} als leere Deklaration an beiden Enden beseitigt \tabcolsep links der ersten und rechts der letzten Spalte, sodass die Tabelle bündig am linken Textrand steht – ein vertrautes Idiom in booktabs-Tabellen. Zweitens ein Trennzeichen einfügen: Zahlen in eine Vorkomma- und eine Nachkommaspalte teilen und @{.} dazwischensetzen; die Punkte stehen dann an fester Stelle, was Dezimalausrichtung ergibt.

Das Gegenstück zu @{…} ist !{decl}. Es steht dort, wo auch eine vertikale Linie | stehen könnte, und fügt statt der Linie ein beliebiges decl ein. Der entscheidende Unterschied: !{…} entfernt den normalen Spaltenzwischenraum nicht. Das Beispiel des array-Handbuchs macht es greifbar: c!{\hspace{1cm}}c ergibt einen Abstand aus vorhandenem Raum plus 1cm, während c@{\hspace{1cm}}c genau 1cm liefert. Wer etwa eine gestrichelte Linie zwischen Spalten möchte, den Zwischenraum aber behalten will (etwa mit einem Befehl aus arydshln darin), greift zu !{…}. Nebenbei ändert das Laden von array auch das Verhalten von |: Der Spaltenabstand wird um die Linienstärke breiter, während die Linie in Standard-LaTeX im vorhandenen Raum sitzt und die Breite gleich bleibt.

Spalten wiederholen und benennen — *{n}{…} und \newcolumntype

Stehen viele gleich deklarierte Spalten hintereinander, fasst *{count}{spec} sie zusammen. Nach der Definition des array-Handbuchs entspricht das count Kopien von spec; sechs zentrierte Spalten werden also zu *{6}{c} statt cccccc. Die wiederholte Einheit darf mehrere Deklarationen enthalten: *{3}{|c}| expandiert zu |c|c|c|. Sein wahrer Wert zeigt sich bei langen Deklarationen mit >{…} oder @{…}*{4}{>{$}c<{$}} baut vier zentrierte Mathematikspalten auf einen Schlag.

Kehrt dieselbe Kombination im ganzen Dokument wieder, liest sich ein Name über \newcolumntype{letter}{…} deutlich besser. Das array-Handbuch führt selbst \newcolumntype{C}{>{$}c<{$}} als Beispiel an; danach genügt {C C C}. \newcolumntype nimmt dasselbe optionale Argument wie \newcommand, sodass sich auch ein Spaltentyp mit der Breite als Parameter bauen lässt. Häufen sich die Definitionen und geht der Überblick verloren, hilft \showcols: Es schreibt die Liste aller derzeit aktiven \newcolumntype-Definitionen ins Protokoll.

Eine Tabelle auf eine Zielbreite bringen – die X-Spalte von tabularx

Bei allen bisherigen Typen bestimmte man selbst die Breite. Der umgekehrte Fall – „die ganze Tabelle soll genau \textwidth breit sein“ – ist der Zweck der X-Spalte von tabularx. Man übergibt zuerst die Zielbreite, etwa \begin{tabularx}{\textwidth}{l X r}, und die mit X markierten Spalten werden rechnerisch so verbreitert oder verschmälert, dass die Differenz aufgeht. X ist deshalb so zugänglich, weil der Mechanismus schlicht ist: Wie das Handbuch schreibt, wird die X-Deklaration nach Berechnung der richtigen Breite in p{some value} umgewandelt. Im Quelltext lautet die Vorgabe schlicht \newcommand{\tabularxcolumn}[1]{p{#1}}. X ist also nur „eine p-Spalte, deren Breite später eingesetzt wird“, und genau deshalb wirken dieselben Verzierungen: >{\raggedright\arraybackslash}X verhält sich erwartungsgemäß. Soll der Inhalt vertikal zentriert stehen, genügt \renewcommand{\tabularxcolumn}[1]{m{#1}}.

document.tex
\usepackage{tabularx}
% ...
% two X columns, the second twice as wide as the first
\begin{tabularx}{\textwidth}{@{}
    l
    >{\hsize=.667\hsize\linewidth=\hsize}X
    >{\hsize=1.333\hsize\linewidth=\hsize}X @{}}
  \hline
  Key & Short note & Longer note \\
  \hline
  alpha & fits in the narrow column & twice as wide, so prose wraps less often \\
  \hline
\end{tabularx}

Standardmäßig werden alle X-Spalten gleich breit; schreibt man aber wie oben >{\hsize=…\hsize\linewidth=\hsize}X, ändern sich ihre Gewichte. Das Handbuch stellt dazu zwei Regeln auf: die Summe der X-Spaltenbreiten unverändert lassen (0,667 und 1,333 oben ergeben zusammen 2, also weiterhin zwei Standard-X-Spalten) und kein \multicolumn verwenden, das eine X-Spalte überquert, da die Breitenberechnung dies nicht berücksichtigt. Läuft etwas schief, hilft die Deklaration \tracingtabularx: Das Protokoll zeigt dann die in jedem Durchgang probierten Spaltenbreiten.

Zwei X-typische Symptome sollte man kennen. Übersteigt die Summe der Nicht-X-Spalten bereits die Zielbreite, weigert sich tabularx, den X-Spalten eine negative Breite zu geben: Es meldet Package tabularx Warning: X Columns too narrow (table too wide) und setzt sie ersatzweise 1em breit, sodass die Tabelle breiter ausfällt als gewünscht. Zweitens wird der Rumpf eines tabularx tatsächlich als Argument eines Befehls gelesen, und die Tabelle wird bei der Breitensuche mehrfach neu gesetzt – deshalb löst ein \verb darin LaTeX Warning: \verb may be unreliable inside tabularx aus. Der Vergleich von tabularx mit tabulary und tabularray sowie der Umgang mit Tabellen, die überhaupt nicht auf die Seite passen, gehören auf die Seite „Breite Tabellen und automatische Breite“.

Ein kombiniertes Beispiel und die moderne Antwort auf Zahlenausrichtung

Zum Schluss eine Spaltendeklaration, die die obigen Werkzeuge bündelt. Spalte 1 ist eine fette Beschriftungsspalte (>{\bfseries}l), Spalte 2 eine linksbündige umbrechende Absatzspalte (>{\raggedright\arraybackslash}p{5cm}), die Spalten 3 und 4 sind zentrierte Mathematikspalten, gemeinsam mit *{2}{…} gebaut, und das äußere Polster wird an beiden Enden mit @{} entfernt. Mehrere Mechanismen in einer Zeile unterzubringen ist die Stärke der Spaltensprache – und zugleich der Grund, warum sie schwer lesbar wird. Deshalb ist es praktisch, häufige Kombinationen mit \newcolumntype zu benennen.

document.tex
\usepackage{array}
% ...
\begin{tabular}{@{} >{\bfseries}l >{\raggedright\arraybackslash}p{5cm} *{2}{c} @{}}
  \hline
  Symbol & Meaning & Value & Unit \\
  \hline
  $c$ & Speed of light in vacuum, a defining constant. & $2.998\times10^{8}$ & m/s \\
  $g$ & Standard gravity, an approximation near the surface. & $9.807$ & m/s$^2$ \\
  \hline
\end{tabular}

Eine Sache sollte man dieser Spaltensprache besser nicht überlassen: die Ausrichtung von Zahlen am Dezimalpunkt. Dafür greift man zur S-Spalte des Pakets siunitx statt zum weiter oben skizzierten @{.}. Die S-Spalte analysiert jede Zahl und richtet Vorkommateil, Dezimalpunkt, Nachkommateil, Exponent und sogar Unsicherheiten korrekt aus und bereinigt nebenbei Ziffergruppierung und signifikante Stellen. Nebenbei druckt das array-Handbuch seinen eigenen Code zur Dezimalausrichtung ab und setzt selbst die Warnung dazu, „the code is bad“, mit Verweis auf eine deutlich bessere Fassung in David Carlisles dcolumn.sty – dass ein Pakethandbuch sein eigenes Beispiel derart offen beurteilt, sieht man selten.