列规格、对齐与宽度

tabular 的第一个引数,即 {lcr} 这样的 列指定,不只是罗列对齐方式的地方。它其实是一门小语言:除了列数和对齐,还能在这里指定让 LaTeX 在每个单元格前后插入声明替换列间空白,或 成组重复列。其中许多功能不属于普通 tabular,而是由 array 添加。本页从基本的 lcrp{width} 出发,依次说明 m{width}b{width}>{…}<{…}@{…}!{…},以及 *{n}{…}

列指定这门小语言

四个基本类型已在另一页介绍。lcr 分别左对齐、居中、右对齐,并按内容的 自然宽度 扩展,不会换行。只有 p{width} 会被排成指定宽度的段落盒,内容超出时会 换行。按照 array 包的内部说法,p{width} 等价于 \parbox[t]{width},也就是 顶部对齐 的段落。这个默认的“顶部”正是接下来两个同类列的对照点。

从这里开始介绍的大多数记号,如 m/b>/<@/!*,都需要在导言区写入 \usepackage{array} 后才能使用。array 随 TeX Live 和 MiKTeX 标准安装,很多表格包(tabularxbooktabs 等)也会在内部载入它,所以它常常已经生效。即便如此,显式载入仍然最稳妥。

段落列的垂直位置 — p、m、b

会换行的段落列有三种,差别只在 单元格的垂直位置,也就是盒子在行内如何垂直对齐。按照 array 手册的定义,p{width}\parbox[t],即 顶部对齐m{width} 把内容 垂直居中到该行b{width}\parbox[b],即 底部对齐mbarray 包添加的,普通 tabular 没有。

它在同一行同时包含高单元格(换成多行的 p 列或大图片)和单行单元格时特别有用。默认 p(顶部)会让相邻的短单元格贴在行顶,视线与高单元格中部对不上。使用 m{width} 时,该列内容会位于行的上下中央,从而与旁边的单行单元格自然对齐。含图片的单元格,或左边标签、右边长说明的表格,都很适合用它。

latex
\usepackage{array}
% ...
\begin{tabular}{ p{3cm} m{3cm} b{3cm} }
  \hline
  上端揃え (p) & 縦中央 (m) & 下端揃え (b) \\
  この列は parbox[t]。 & この列は行の上下中央に。 & この列は parbox[b]\\
  \hline
\end{tabular}

即使宽度和正文完全相同,pmb 也会改变各单元格的上下位置。还要注意,在这三种段落列内部,\parindent(段落缩进)默认是 0(需要时可用 >{\setlength{\parindent}{1em}} 等方式加回来)。

指定符垂直位置等价 parbox提供者
p{wd}顶部\parbox[t]{wd}标准
m{wd}中部在行内垂直居中array
b{wd}底部\parbox[b]{wd}array

在单元格前后插入 — >{…} 与 <{…}

array 包的核心功能是 >{decl}<{decl}>{decl} 会在该列 每个单元格内容之前 立即插入 decl<{decl} 则在内容 之后 插入。比如写 >{\bfseries}l,该左对齐列的所有单元格都会变成 粗体,不必在每个单元格中单独写 \bfseries>{\itshape}c 得到居中斜体,>{\color{red}}r 得到右对齐红色,依此类推。

>{…} 中也可以放 对齐声明。段落列(pmb)默认两端对齐,但 >{\raggedright}p{4cm} 会生成 左齐(右边不齐) 的列,>{\centering} 生成居中列,>{\raggedleft} 生成右齐段落列。这就能满足“要换行,但不想两端对齐”的细微需求。

不过这里有一个 严重陷阱\raggedright\raggedleft\centering 三个命令会在内部 重新定义行结束符 \\。如果使用这些声明的 >{…} 列位于表格 最后一列\\ 就不再作为“行结束”工作,可能导致错误或输出错乱。解决方法是在声明 最后 加上 array 提供的 \arraybackslash:写 >{\raggedright\arraybackslash}p{4cm} 就能恢复 \\ 的行结束功能。\centering\raggedleft 也同样需要。

另一个常用技巧是让 整列进入数学模式。写 >{$}c<{$} 会在每个单元格前后分别插入 $,于是该列所有单元格都会作为 行内数学 排版;数字或符号很多的列就不用每格都写 $…$。反过来,在本身已经处于数学模式的 array 环境中使用 >{$}…<{$},两个 $ 会相互抵消,使 只有该列进入文本模式

需要注意的是,>{…}<{…} 中应写 声明(declaration)。像 >{\bfseries}>{\itshape} 这样后面不再取引数的形式没有问题,但 不能以需要引数的命令结尾。例如 >{\textbf} 会试图把后续单元格内容当作它的引数读取,从而出错(想加粗应使用声明形式 \bfseries)。array 手册也提醒,>{\textbf} “既不会让整列变粗,也不会让第一个字符变粗”。

调整列边界 — @{…} 与 !{…}

LaTeX 默认会在两列之间插入 \tabcolsep 的空白(默认每侧 6pt,两侧合计为两倍)。把 @{decl} 放在列指定的那个位置,会 移除那里的列间空白,并改为插入 decl(用 array 手册的话说,就是“抑制 inter-column space,并插入 decl.”)。

主要有两种用途。第一是 裁掉表格外侧留白:在列指定两端放 @{}(空声明),会移除第一列左侧和最后一列右侧的 \tabcolsep,使表格与正文左边距齐平;这是 booktabs 表格中常见的写法。第二是 插入分隔字符:若要按小数点对齐数字,可把数字分成整数部分列和小数部分列,并在中间放 @{.},这样小数点就会固定位置对齐,得到 小数点对齐

@{…} 相对应的是 !{decl}。它可以写在竖线 | 的位置,并用任意 decl 代替竖线。它与 @{…} 的决定性区别是:!{…} 不会移除普通列间空白。因此它适合“想在列之间画虚线,但保留列间内边距”的情况,例如把 arydshln 包的虚线命令放进 !{…}

顺带一提,载入 array 后,竖线 | 的行为也会稍有变化:列间距会按线条粗细 变宽(在标准 LaTeX 中,线条位于已有空白内,列间距不会增加)。不过专业表格中通常应避免竖线本身,这个话题留到“线条与出版级表格”页面讨论。

重复列 — *{n}{…}

当许多相同指定的列连续出现时,可以用 *{count}{spec} 折叠重复。按照 array 手册的定义,它等价于“speccount 份拷贝”。例如六个居中列可以写 *{6}{c},而不是 cccccc。重复单元也可以包含多个指定:*{3}{|c}| 会展开为 |c|c|c|。当要重复包含 >{…}@{…} 的长指定时尤其有用,例如 *{4}{>{$}c<{$}} 一次生成四个居中的数学模式列。

组合多个机制的示例

最后,下面这个例子把前面工具塞进一个列指定中。第 1 列是粗体标签列(>{\bfseries}l);第 2 列是左齐且会换行的段落列(>{\raggedright\arraybackslash}p{5cm},这里它不是最后一列,所以并非严格必需,但习惯上仍加 \arraybackslash);第 3、4 列用 *{2}{…} 一起生成居中的数学模式列;两端的 @{} 则裁掉外侧留白。

document.tex
\usepackage{array}
% ...
\begin{tabular}{@{} >{\bfseries}l >{\raggedright\arraybackslash}p{5cm} *{2}{c} @{}}
  \hline
  記号 & 意味 & 値 & 単位 \\
  \hline
  $c$ & 真空中の光速。物理定数のひとつ。 & $2.998\times10^{8}$ & m/s \\
  $g$ & 標準重力加速度。地表付近での近似値。 & $9.807$ & m/s$^2$ \\
  \hline
\end{tabular}

结果是:符号列统一为粗体,含义列在 5cm 宽度内左齐换行,两列数值居中排列,整张表没有外侧留白并与正文左端齐平。列语言的强项就是能在一行中写入多个机制,但也容易变得难读。因此常用组合最好用 \newcolumntype{yourletter}{…} 命名为 自定义列类型,之后就能像 {C C C} 这样简洁复用(这是 array 包的功能)。

顺带一提,用来按小数点对齐数字时,现代且可靠的选择是 siunitx 包的 S,而不是这里提到的 @{.}S 列会解析每个数字,并正确对齐整数部分、小数点、小数部分、指数,甚至不确定度,同时整理分组和有效数字格式。请与“宽表与自动宽度”页面一起参照。