Especificación de columnas, alineación y anchura

El {lcr} de \begin{tabular}{lcr} es uno de los pocos lugares de LaTeX donde se escribe un programa en vez de texto. Más allá del número de columnas y su alineación, puedes indicar que se inserte una declaración antes y después de cada celda, que se sustituya el espacio entre columnas por otra cosa o que se repita un grupo de columnas. Y el vocabulario de esta especificación de columnas no salió de un único diseño: se fue acumulando por donaciones. \newcolumntype nació en un paquete aparte de David Carlisle, newarray.sty, y el manual de array deja constancia de que «David kindly agreed on the inclusion». Partiendo de l, c, r y p{width}, esta página recorre el lenguaje a través de m{width} y b{width}, >{…} y <{…}, @{…} y !{…}, *{n}{…}, w y W, hasta la columna X de tabularx que hace que una tabla alcance un ancho exacto.

Qué especificadores necesitan qué paquete

LaTeX a secas solo da l, c, r, p{width}, |, @{…} y *{n}{…}. Todo lo demás — m{width}, b{width}, >{…}, <{…}, !{…}, w y W, y \newcolumntype — viene del paquete array (TeX Live 2024 incluye la v2.5g del 16 de octubre de 2023, de Frank Mittelbach). La columna X viene de tabularx (v2.11c del 8 de julio de 2023, de David Carlisle), que a su vez requiere array. Como booktabs y tabularx cargan array internamente, a menudo ya está activo, pero escribir \usepackage{array} de forma explícita es lo seguro.

Saber cuál está en marcha en tu documento se resuelve en un paso: escribe adrede una letra de columna que no existe. LaTeX a secas responde ! LaTeX Error: Illegal character in array arg., mientras que con array cargado la queja viene del paquete: ! Package array Error: Illegal pream-token. Así que si escribiste m{width} y te dijeron “Illegal character”, el mensaje te está diciendo en realidad que olvidaste cargar array.

Columnas de párrafo p, m, b, y las de ancho fijo w y W

Hay tres columnas de párrafo con ajuste y solo difieren en la posición vertical de la celda. En palabras del manual de array, p{width} equivale a \parbox[t]{width} y va alineada arriba; m{width} coloca su contenido en el centro vertical de la fila; y b{width} «coincides with \parbox[b]», es decir, alineada abajo. La diferencia se nota cuando una fila mezcla una celda alta —una columna p ajustada a varias líneas, o una imagen grande— con celdas de una sola línea. Con el p predeterminado, la vecina corta se pega arriba y no queda a la altura del centro de la celda alta; m{width} las alinea de forma natural.

Cuando quieres ancho fijo pero sin ajuste de línea, array ofrece w{align}{width} y W{align}{width}. Ambas funcionan esencialmente como \makebox[width][align]{cell}: se comportan como l, c o r mientras fijan el ancho de la columna. Se diferencian en cómo fallan: w se superpone en silencio cuando el contenido es más ancho que la medida, mientras que W emite un aviso de caja desbordada en la misma situación. Eso sí, cuando W desborda siempre sobresale hacia la derecha, de modo que la alineación aparente cambia. Si el requisito es «fijar el ancho pero avisarme si se desborda», W es la respuesta correcta.

EspecificadorPosición vertical / ancho¿Se ajusta?Proporcionado por
p{wd}Arriba; equivale a \parbox[t]{wd}Estándar
m{wd}Centrado verticalmente en la filaarray
b{wd}Abajo; coincide con \parbox[b]{wd}array
w{l}{wd}Como \makebox[wd][l]; se superpone en silencioNoarray
W{l}{wd}Como w pero avisa; el desbordamiento va a la derechaNoarray
XColumna p cuyo ancho se calcula para el objetivotabularx

Dos pequeños valores por omisión conviene recordar al usar columnas de párrafo. Primero, dentro de p, m y b la sangría \parindent es 0; para recuperarla, escribe algo como >{\setlength{\parindent}{1em}}p{5cm}. Segundo, array añade una longitud llamada \extrarowheight. Con un valor positivo aumenta solo la altura de cada fila y deja la profundidad intacta: la receta más directa contra las reglas \hline que parecen tocar la parte alta de las mayúsculas. El manual de array señala que fijó 1pt para la propia tabla impresa en su documentación.

Insertar alrededor de cada celda — >{…} y <{…}

La pieza central del paquete array son >{decl} y <{decl}. >{decl} inserta decl justo antes del contenido de cada celda de esa columna; <{decl}, justo después. Escribe >{\bfseries}l y todas las celdas de una columna alineada a la izquierda salen en negrita sin teclear nada celda a celda. >{\itshape}c da cursiva centrada, >{\color{red}}r rojo alineado a la derecha. En columnas de párrafo también caben declaraciones de alineación: >{\raggedright}p{4cm} crea una columna que «se ajusta pero no se justifica».

Acecha, sin embargo, una trampa seria. \raggedright, \raggedleft y \centering redefinen internamente el terminador de fila \\. Así que si una columna que lleva uno de ellos en >{…} es la última columna de la tabla, \\ deja de terminar filas, el & de la fila siguiente se desborda y aparece ! Extra alignment tab has been changed to \cr.. La causa está en la especificación, pero el error señala una línea del cuerpo, por lo que nadie lo detecta a la primera. El remedio es añadir el \arraybackslash de array al final de la declaración: >{\raggedright\arraybackslash}p{4cm} restaura el terminador. En una columna que no es la última no hace daño, así que lo prudente es escribirlo siempre.

El origen de \arraybackslash tiene un giro. Lo introdujo tabularx, precisamente para reparar este choque con \raggedright. Hoy es array.sty quien lleva \def\arraybackslash{\let\\\tabularnewline}, mientras que tabularx conserva solo una definición de respaldo condicional. Dicho de otro modo: la herramienta nació en un paquete y ahora vive en otro.

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}

El >{$}c<{$} de la tercera columna es justo el propósito original de > y <. Cada celda recibe un $ antes y después, así que toda la columna se compone como matemáticas en línea y ya no se teclea $…$ en columnas llenas de números o símbolos. A la inversa, dentro de un entorno array, cuyas celdas ya están en modo matemático, los dos $ se cancelan y ponen solo esa columna en modo texto. La fila de encabezado, eso sí, pide cuidado: escribir Value tal cual lo compone como fórmula y sale en cursiva con espaciado suelto. Como en el ejemplo anterior, \multicolumn{1}{c}{Value} devuelve esa única celda al modo texto.

Recuerda también la restricción: en >{…} y <{…} solo cabe una declaración. Las formas que no toman argumento después, como \bfseries o \itshape, están bien, pero no debes terminar con un comando que toma argumento. >{\textbf} empieza a leer el contenido de la celda como su argumento y falla del modo más incómodo: sin error, y nada sale en negrita. El manual de array lo dice sin rodeos: >{\textbf} «would not make the whole column bold nor would it make the first character bold». Para negrita, usa la forma declarativa \bfseries.

En el límite entre columnas — @{…} y !{…}

Entre dos columnas, LaTeX inserta por omisión espacio \tabcolsep: 6 pt por lado, 12 pt en total. Pon @{decl} en esa posición de la especificación y, en palabras del manual de array, «suppresses inter-column space and inserts decl. instead»: el hueco desaparece y lo que escribiste ocupa su lugar. Hay dos usos principales. Primero, recortar el relleno exterior de la tabla: @{} (declaración vacía) en ambos extremos elimina el \tabcolsep a la izquierda de la primera columna y a la derecha de la última, de modo que la tabla queda alineada con el borde izquierdo del texto, un modismo familiar en tablas con booktabs. Segundo, insertar un separador: divide los números en una columna de parte entera y otra de parte fraccionaria y pon @{.} entre ellas; los puntos quedan en posición fija y se obtiene alineación decimal.

La contraparte de @{…} es !{decl}. Va donde podría ir una regla vertical | e inserta un decl arbitrario en lugar de la regla. La diferencia decisiva es que !{…} no elimina el espacio normal entre columnas. El propio ejemplo del manual de array lo hace concreto: c!{\hspace{1cm}}c da una separación igual al espacio existente más 1 cm, mientras que c@{\hspace{1cm}}c da exactamente 1 cm. Cuando quieres, por ejemplo, una línea discontinua entre columnas pero conservando el relleno (metiendo dentro un comando de arydshln), !{…} es la elección correcta. Por cierto, cargar array también cambia el comportamiento de |: la separación entre columnas aumenta por el grosor de la regla, mientras que en LaTeX estándar la regla se aloja en el espacio existente y el ancho no cambia.

Repetir y nombrar columnas — *{n}{…} y \newcolumntype

Cuando muchas columnas con la misma especificación van seguidas, *{count}{spec} las pliega. Según la definición del manual de array, equivale a count copias de spec, así que seis columnas centradas se convierten en *{6}{c} en vez de cccccc. La unidad repetida puede contener varios especificadores: *{3}{|c}| se expande a |c|c|c|. Su verdadero valor se ve cuando la especificación repetida es larga y contiene >{…} o @{…}: *{4}{>{$}c<{$}} construye cuatro columnas matemáticas centradas de un golpe.

Si la misma combinación se repite por todo el documento, ponerle nombre con \newcolumntype{letter}{…} se lee mucho mejor. El propio manual de array ofrece \newcolumntype{C}{>{$}c<{$}} como ejemplo; después basta con {C C C}. \newcolumntype acepta el mismo argumento opcional que \newcommand, así que también puedes construir un tipo de columna que tome el ancho como parámetro. Cuando las definiciones se acumulan y pierdes el hilo, escribe \showcols: vuelca al registro la lista de todas las definiciones \newcolumntype activas.

Ajustar una tabla a un ancho objetivo: la columna X de tabularx

Hasta aquí, el ancho lo decidías . El caso contrario —«que toda la tabla mida exactamente \textwidth»— es para lo que sirve la columna X de tabularx. Pasa primero el ancho objetivo, como en \begin{tabularx}{\textwidth}{l X r}, y las columnas marcadas con X se ensanchan o estrechan por cálculo para cubrir la diferencia. Lo que hace accesible a X es lo sencillo del mecanismo: como dice el manual, la especificación X se convierte en p{algún valor} una vez calculado el ancho correcto. En el código, el valor por omisión es una sola línea: \newcommand{\tabularxcolumn}[1]{p{#1}}. X no es, pues, más que «una columna p cuyo ancho se rellena después», y por eso funcionan sobre ella las mismas decoraciones: >{\raggedright\arraybackslash}X se comporta como esperas. ¿Quieres el contenido centrado verticalmente? Redefínelo: \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}

Por omisión, todas las columnas X acaban con el mismo ancho, pero escribir >{\hsize=…\hsize\linewidth=\hsize}X como arriba cambia sus pesos. El manual impone dos reglas: mantener sin cambios la suma de los anchos de las columnas X (0,667 y 1,333 suman 2, es decir, siguen siendo dos columnas X estándar) y no usar un \multicolumn que cruce ninguna columna X, porque el cálculo del ancho no lo contempla. Si algo se comporta mal, declara \tracingtabularx y el registro imprimirá los anchos probados en cada pasada.

Conviene conocer dos síntomas propios de X. Si las columnas que no son X ya suman más que el ancho objetivo, tabularx se niega a dar a las columnas X un ancho negativo: emite Package tabularx Warning: X Columns too narrow (table too wide) y las fija en 1 em, con lo que la tabla sale más ancha de lo pedido. Segundo, el cuerpo de un tabularx se lee en realidad como argumento de un comando y la tabla se compone varias veces mientras se buscan los anchos, por lo que un \verb dentro provoca LaTeX Warning: \verb may be unreliable inside tabularx. La comparación entre tabularx, tabulary y tabularray, y qué hacer con tablas que no caben en la página, corresponden a «tablas anchas y ancho automático».

Un ejemplo combinado y la respuesta moderna para alinear números

Por último, una especificación que reúne las herramientas anteriores. La columna 1 es una columna de etiquetas en negrita (>{\bfseries}l); la 2, una columna de párrafo ajustable alineada a la izquierda (>{\raggedright\arraybackslash}p{5cm}); las columnas 3 y 4 son columnas matemáticas centradas construidas juntas con *{2}{…}; y el relleno exterior se recorta con @{} en ambos extremos. Meter varios mecanismos en una línea es la fuerza del lenguaje de columnas, y también lo que lo vuelve ilegible: de ahí que nombrar una combinación frecuente con \newcolumntype sea lo práctico.

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}

Una cosa para la que este lenguaje de columnas es mejor no usarse: alinear números por el punto decimal. Recurre a la columna S del paquete siunitx en vez del @{.} esbozado antes. La columna S analiza cada número y alinea correctamente parte entera, punto decimal, parte fraccionaria, exponente e incluso incertidumbres, y de paso ordena la agrupación de cifras y las cifras significativas. Por cierto, el manual de array imprime su propio código de alineación decimal y luego le adjunta la advertencia de que «the code is bad», remitiendo a una versión mucho mejor en el dcolumn.sty de David Carlisle: que el manual de un paquete juzgue así su propio ejemplo es cosa rara.