高级表格环境

普通 tabular 会把每一列扩展到其内容的自然宽度。因此一旦想让整张表精确等于正文宽度,或让包含长文本的多列等宽排列,就会很难处理。解决这个问题的包包括 tabularx(把列伸缩到目标宽度)、tabulary(按每列内容多少分配宽度),以及近年来逐渐成为标准的 tabularraytblr 环境,一个通过键值选项控制表格所有方面的现代包)。本页介绍三者的区别,并给出各自的实例。

宽度问题

tabular 中,lcr 列不会换行,p{width} 列会换行,但它要求你 自己计算并指定宽度。如果有三列,希望表格正好填满正文宽度 \textwidth,凭感觉写 p{0.3\textwidth} 等会忽略线条和列间空白(\tabcolsep)的宽度,结果不是超出就是留空。

因此换一种思路:先固定总宽度,再让列去适应,这就是 tabularx按每列内容多少自动分配宽度,这就是 tabulary。二者都是 David Carlisle 的经典包(tabularx 属于 tools bundle),分别用 \usepackage{tabularx}\usepackage{tabulary} 载入。

tabularx — 伸缩到固定宽度的 X 列

tabular 不同,tabularx 环境的 第一个引数是整张表的目标宽度\begin{tabularx}{width}{column spec}。列指定中可以使用新的列类型 X,它是一个会自动伸缩以填满剩余宽度的 p 风格列。如果有多个 X 列,它们会 平均分享可用宽度

它的机制有点特别:tabularx 会通过 多次试排表格 来求宽度。它先把每个 X 列都按完整目标宽度排出(此时表格过宽),再把超出的部分按 X 列数量分摊,缩小每个 X 列并重新排版,反复迭代直到收敛。最终,X 会被内部的 \tabularxcolumn 所定义的列替换;默认定义如下。

latex
\newcommand{\tabularxcolumn}[1]{p{#1}}

因此 X 默认是 顶部对齐 的段落列。若想让内容 垂直居中,可重定义为 \renewcommand{\tabularxcolumn}[1]{m{#1}}X 列内容默认两端对齐;若要改为左齐,可在列指定中加前缀 >{\raggedright\arraybackslash}X。这里的关键是 \arraybackslash\raggedright\centering\raggedleft 会重新定义 \\ 的含义,与表格的行结束 \\ 冲突,所以要在这些声明后放 \arraybackslash,把 \\ 恢复为行结束符。

如果反复使用同一前处理,可用 \newcolumntype 给它起一个单字母别名,提高可读性。下面的例子把第 1 列设为自然宽度的左对齐 l,其余两列设为 等宽、左齐且会换行的 Y,并让整张表适配 \textwidth

document.tex
\usepackage{tabularx}
\newcolumntype{Y}{>{\raggedright\arraybackslash}X}
% ...
\begin{tabularx}{\textwidth}{l Y Y}
  \hline
  項目 & 長所 & 短所 \\
  \hline
  tabular  & 単純で速い & 幅を自分で調整する必要がある \\
  tabularx & 表幅を固定でき、列が自動で伸縮する & 試し組みのぶん処理が遅い \\
  \hline
\end{tabularx}

这张表会铺满整个正文宽度(\textwidth);第 1 列“Item”保持短词的自然宽度,第 2、3 列各自取得 剩余宽度的一半,长说明会在该宽度内换行。由于使用了 Y(左齐的 X),每个单元格右边缘会不齐。如果想让列宽比例不等,后面介绍的 tabularray 系数语法(如 X[2])很方便;普通 tabularx 没有系数选项。

需要注意的是,由于表体会被展开多次,tabularx 中的 带副作用命令(推进计数器、写文件、\verb 等)可能误动作。必须包含脚注或 \verb 的单元格,通常需要先放进一个盒子等方式处理。

tabulary — 按内容分配宽度

tabularx 会在各个 X 列之间 平均 分配空间。但如果列之间内容量差异很大,例如一列是短标签、另一列是长说明,平均分配反而不自然。tabulary 则会 按每列内容的自然宽度成比例 分配宽度,并把总宽度缩放到你指定的值。

语法与 tabularx 类似:\begin{tabulary}{width}{column spec}。它有四种列类型,都是自动宽度的段落列:L 左对齐,C 居中,R 右对齐,J 两端对齐。也可以混用普通的 lcrp{width} 等,它们会 排除在测量之外,保持自然宽度。被测列会按该列最宽单元格的自然宽度加权,并缩放到整张表达到请求宽度;但自然宽度已经较小的列会保持原样。

为了避免极端分配,可以用两个长度限制被测列:最小宽度 \tymin(默认 10pt)和 最大宽度 \tymax(默认 2\textwidth)。当某个特别长的词会独占列宽时,这些限制很有用。

document.tex
\usepackage{tabulary}
% ...
\begin{tabulary}{\textwidth}{L L}
  \hline
  用語 & 説明 \\
  \hline
  X 列 & 表幅から固定部分を引いた残りを、X 列どうしで均等に分け合う列。 \\
  \hline
\end{tabulary}

这里两列都是 L(左对齐),但第 1 列“Term”内容短,所以较窄;第 2 列“Description”是长文本,所以获得更大的宽度。如果用 tabularxX X,两列会等宽;而 tabulary 遵循 内容权重,因此标签列加正文列这样的表格会得到自然比例。注意 tabulary 也会处理主体两次,所以脚注和计数器需要像 tabularx 中一样小心。

tabularray — 现代 tblr 环境

tabularray 是近年来开发最活跃的表格包,作者是 吕建瑞(Jianrui Lyu,lvjr)。它抛弃旧的基于 \halign 的机制,使用 LaTeX3(expl3)直接解析并排版表格。其最大特点是把过去需要 arraybooktabsmultirowcolortbldiagbox 等多个包配合完成的 线条、颜色、单元格跨越、段落格式和列宽,统一到 一个键值接口 中。用 \usepackage{tabularray} 载入,主环境是 tblr

tblr 的结构与 tabular 略有不同:在主体之前,要像 \begin{tblr}{ … inner spec … } 那样给出 一个由逗号分隔键值选项组成的引数(内侧指定)。列对齐用 colspec 键声明。可以写熟悉的一字母形式 colspec={lcr},但真正强大的是 Q[…](通用列,在方括号中传入对齐、宽度等选项)和 X[…](类似 tabularx 的伸缩列)。X[c] 是居中的伸缩列;X[2,l]系数 2(普通 X 两倍宽)的左对齐伸缩列。

使用 X 列时,用 width 键给出它们要扩展到的宽度(未指定时默认 \linewidth)。行间和列间空白由 rowsepcolsep 键控制,tblr 默认是 rowsep=2ptcolsep=6pt。线条是声明式的:写 hlines / vlines 会画出所有横线 / 竖线,而 hline{n} / vline{n} 可设置特定线条的粗细和类型,如 hline{2}={1pt,solid}。主体中不需要写 \hline

单元格跨越在主体中用 \SetCell 完成。\SetCell[c=2]{c}跨 2 列 的居中单元格(相当于 \multicolumn),\SetCell[r=2]{c}跨 2 行 的单元格(相当于 \multirow);c=r= 给出列 / 行跨越数,花括号参数给出该单元格的对齐。若要装饰整行或整列,使用 \SetRow / \SetColumn,例如 \SetRow{cyan8} 会把行涂成淡青色(颜色名是包内置的)。

document.tex
\usepackage{tabularray}
% ...
\begin{tblr}{
  width = \textwidth,
  colspec = {Q[l] X[c] X[2,l]},
  rowsep = 3pt,
  hline{1,Z} = {1pt,solid},
  hline{2} = {0.5pt,solid},
  row{1} = {font=\bfseries},
}
  項目 & 区分 & 説明 \\
  \SetCell[r=2]{l} 表環境 & 固定幅 & 表全体の幅を固定し、X 列が残りを分け合う。 \\
   & 内容幅 & 各列の中身の量に応じて幅を配分する。 \\
  注記 & --- & 罫線・色・結合をキーで一括指定できる。 \\
\end{tblr}

这个 tblrwidth=\textwidth 把表宽固定为正文宽度。三列分别是 Q[l](左对齐基本列)、X[c](居中伸缩列)和 X[2,l](系数 2、左对齐伸缩列),所以两个 X 列按 1 : 2 分配剩余宽度,第 3 列“Description”最宽。主体中没有 \hline;线条只来自内侧指定中的 hline{…} 键(Z 是表示最后一行的特殊编号)。首行通过 row{1}={font=\bfseries} 加粗,第 1 列的“Table environment”用 \SetCell[r=2]{l} 跨 2 行。无需同时引入 booktabsmultirowarray 就能完成这些,正是 tabularray 的吸引力。

对于跨页长表,可以使用 longtblr 环境,它支持重复表头、标题和脚注;如果想做类似 threeparttable 的单页带注表格,则使用 talltblr。在这些环境中,标题、标签、note{…} 等写在主体之前的 可选参数 […](外侧指定) 中。

该选择哪一个

经验法则如下:若要让表格 贴合固定宽度,并让长文本列 等宽 排列,用 tabularx;若同样固定宽度,但想 按每列内容量自然分配,用 tabulary;若是新写表格,并希望把线条、颜色、单元格跨越、列宽都 用一个系统 统一处理,用 tabularray

tabularray 功能丰富,并通过 X 列加 width 键吸收了 tabularx / tabulary 解决的宽度问题。相对地,tabularxtabulary 成熟、轻量,并能自然融入已有 tabular 代码。可以这样理解:给现有短表添加宽度控制时用传统包;从头认真构建复杂表格时用 tabularray

宽度如何确定适合用途
tabularx固定总宽;X 列平均分配适配正文宽度的等宽文本列
tabulary固定总宽;按内容分配内容量不均的标签+正文表
tabularraywidth 键 + X 列(可用系数)统一线条、颜色和跨越的现代表格

列指定本身的细节(\multicolumnarray 包的 >{…} 等)见“列指定”页面,单元格合并基础见“合并单元格”页面,跨页长表见“longtable”页面。