表格线

LaTeX 排出的表格之所以一眼就能被认出是「LaTeX 表格」,最大的原因就是表格线。用 \hline| 画出的线看起来紧贴着文字——这不是主观印象,而是可以量出来的:\hline 塞进行间的只有它自己 0.4pt 的线宽,上下留白为 。而 booktabs\midrule 除了 0.5pt 的线,还会带来 4.52pt 的空白。把学术期刊的表格和原生 LaTeX 表格区分开来的,正是这几个点的留白。本页依次讲解标准的 \hline\cline\vlinebooktabs\toprule\midrule\bottomrule,以及处理二者都够不到的交叉点的 hhline,全部附上实测数值。

标准表格线 \hline、\cline、| 为什么显得局促

答案是:它们完全不增加留白。实测一个两行的 tabular,无线时高 24.0pt;在两行之间加一条 \hline 后变成 24.4pt。增加的部分 恰好等于 \arrayrulewidth(默认 0.4pt),也就是线本身的粗细,没有任何额外空白。这正是线看起来贴着文字的原因。\cline 更极端:加上 \cline{2-3} 后高度仍是 24.0pt,增量 正好为零。线并不把两行撑开,而是画在原本就有的行距里。

这些工具本身很直白。\hline 放在一行的 \\ 之后(或表格最上方、最下方),会画一条贯穿全宽的线;连写两个 \hline\hline 就得到由 \doublerulesep(默认 2pt)隔开的双线。只想给部分列画线,用 \cline{i-j}——\cline{2-3} 只画在第 2、3 列下方,不相邻的范围就写成 \cline{1-1}\cline{3-4}纵线属于列指定,而不是表体:写成 {|l|c|r|} 会在每个 | 处放一条贯穿表高的纵线,|| 则是双纵线。此外还有在单个单元格内画竖线的 \vline|\hline\vline 的粗细统一由 \arrayrulewidth 掌管,因此 \setlength{\arrayrulewidth}{0.8pt} 会让表中所有线同时变粗。

latex
% every standard ruling feature at once -- and a specimen of what to avoid
\begin{tabular}{|l|c|r|}
  \hline
  Item & Qty & Price \\
  \hline\hline
  Apple & 3 & 380 \\
  \cline{2-3}
  Orange & 12 & 120 \\
  \hline
\end{tabular}

这个例子用纵线 | 围住所有列,用 \hline\hline 在表头下方画双线,中间用 \cline{2-3} 分隔。它把标准功能全用了一遍——同时也正是 应当避免的过度画线的样本。在排版传统中,线不是关住数据的笼子,而是标示信息逻辑分隔的克制工具。纵线在列之间筑墙,挡住视线的移动;双线让版面变重,却什么也换不来。如果坚持使用原生 \hline,至少 不要把表格框起来:只在表头下方以及表格上下画线,其余一概不画。下一节的 booktabs,就是把这套规矩固化成宏包。

booktabs 的用法——\toprule、\midrule、\bottomrule

在导言区写 \usepackage{booktabs},把 \hline 换成三种线即可:表格最上方 \toprule,表头与正文之间 \midrule,最下方 \bottomrule。仅此就得到一张没有纵线也没有双线的期刊式表格。起作用的有两点:把线粗分成三级,以及 在每条线上下加入留白。数字最能说明问题——同样三行的表格,\hline 版高 37.20pt,booktabs 版高 47.14pt。内容完全相同,却多出 约 10pt 的留白

默认值写在 booktabs.sty 里:\heavyrulewidth=.08em(用于 \toprule\bottomrule)、\lightrulewidth=.05em\midrule)、\cmidrulewidth=.03em\cmidrule)。在 10pt 文档中实测依次为 0.80002pt、0.50003pt、0.29999pt,正好从上下两侧夹住 \hline 的 0.4pt。设计的巧妙之处在于单位:由于以 em 给出,11pt 文档中粗线为 0.876pt,12pt 文档中为 0.94pt,也就是 线粗随正文字号成比例变化。而 \arrayrulewidth 不论文档类始终是 0.4pt,仅凭这一点 \hline 就与正文不相称。留白方面是 \aboverulesep=.4ex(10pt 文档中 1.72218pt)与 \belowrulesep=.65ex(2.79857pt)。因此一条 \midrule 给行增加的高度是 0.5+1.72+2.80 = 5.02pt,超过 \hline 那 0.4pt 的十二倍。

latex
\usepackage{booktabs}

% \toprule goes right after \begin{tabular}{...}; the rest follow a row's \\
\begin{tabular}{@{}llr@{}}
  \toprule
  \multicolumn{2}{c}{Fruit} \\
  \cmidrule(lr){1-2}
  Kind & Grade & Price \\
  \midrule
  Apple  & premium & 380 \\
  Orange & second  & 120 \\
  \addlinespace
  Total  &         & 500 \\
  \bottomrule
\end{tabular}

合计行前面那一点空隙来自 \addlinespace。把它放在一行的 \\ 之后,会插入 \defaultaddspace.5em,即 10pt 文档中的 5pt),也可以用 \addlinespace[1ex] 指定具体数值。它比用 \\[2pt] 撑高一行更能表达意图,放在线条旁边时间距也更准确。只有当你想完全自己决定粗细与上下留白时,才需要 \specialrule{width}{above}{below}(三个参数都必填)——这是为特殊情形准备的命令,平时三条线就够用了。另外,线必须放在行的分界处:如果在单元格中间写 \toprule,编译会以 ! Misplaced \noalign. 中断,因为和 \hline 一样,这些命令是在 \noalign 内部工作的。

命令 / 长度作用默认值(10pt 文档实测)
\toprule表格最上方的线\heavyrulewidth = .08em = 0.80002pt
\midrule分隔表头与正文的线\lightrulewidth = .05em = 0.50003pt
\bottomrule表格最下方的线\heavyrulewidth = .08em = 0.80002pt
\cmidrule(lr){a-b}只跨第 a–b 列的部分线\cmidrulewidth = .03em = 0.29999pt;修剪量 \cmidrulekern = .5em = 5pt
\aboverulesep每条线上方插入的留白.4ex = 1.72218pt
\belowrulesep每条线下方插入的留白.65ex = 2.79857pt
\addlinespace在两行之间额外加入的留白\defaultaddspace = .5em = 5pt
\specialrule粗细与上下留白全部显式指定的线三个参数均为必填(无默认值)

顺带一提,\heavyrulewidth 里用 width 表示 粗细,并不是英语用错了。booktabs.dtx 专门写了一段术语说明:在英国的排版行业中,line 一律称作 rule,而 rule 的 thickness「常被称为它的 width」——作者还揶揄道,换成别人谈起横线时大概会说 depth 或 height。他把这套行话原样带进了命令名。另外还有一件事:TeX Live 2024 所附 booktabs 的版本号是 1.61803398,正是黄金比 φ。.dtx 标题页的脚注里,作者亲笔写道版本号「正收敛于 phi,即黄金比」,每发布一次就在末尾加一位数字。摘要中列出了 1.618、1.6180、1.61803、1.618033,脚注注明这些修 bug 版本是 Danie Els 在 作者缺席期间 发布的。

\cline 与 \cmidrule 的区别

可见的差别就在于 留白与端部处理。在同一个两行表格上实测,\cline{2-3} 增加的高度是 0.0pt,而 \cmidrule{2-3} 增加 4.82pt(线宽 0.29999pt + \aboverulesep 1.72218pt + \belowrulesep 2.79857pt)。这正是 \cline 看起来像啃进文字里的原因。另一个差别是端部。\cmidrule 默认会延伸到所跨列的完整宽度,容易和相邻的部分线连在一起,因此要在圆括号中给出 修剪(l)(r)(lr) 会把左端、右端或两端收进 \cmidrulekern.5em = 5pt),也可以像 (r{.75em}) 那样指定数值。完整语法是 \cmidrule 后依次跟方括号中的粗细、圆括号中的修剪量,以及 {a-b}。作为经验法则,凡是用 \multicolumn 归组的列标题,下面几乎总应该配一条 \cmidrule(lr){…}

但绝不能混用。作者明确写道,这些命令「不保证能与 \hline\cline 一起工作……我想不出有什么理由要把它们混着用」。事实上,booktabs 的线被刻意设计成 不与列指定中的 | 相连,而这正是一项特性(a feature)。只有在确实需要同一高度上的双重部分线时,才在两条 \cmidrule 之间插入 \morecmidrules\cmidrule{1-2}\morecmidrules\cmidrule{1-2};间距由 \cmidrulesep 决定,默认与 \doublerulesep 相同,为 2pt)。既然双线本身就不被推荐,这同样不是日常工具。

为什么不该使用纵线

booktabs 的说明书用祈使句写下这一点,毫不含糊。它说,只要始终记住两条简单的准则,就不会偏离太远——「1. 绝对、绝对不要使用纵线。2. 不要使用双线。」 接着写道:「这些准则也许看起来偏激,但我从未找到过打破它们的好理由。」如果表格左半与右半差异大到想用一条竖线隔开,作者的答案是 应该改用两张表。在另一处他说得更短:不要在表格中使用纵线,就这样。

同一份文档里还列出了 另外三条准则,作者称它们「在职业排版者和文字编辑的圈子之外通常不为人知」,而且相当实用。把单位写在列标题里,不要写进表格正文。小数点前必须有数字——写 0.1,不要写 .1不要使用「同上」之类的重复记号:多数情况下留空即可,如果不行就把数值重复写出来。作者对自己的规则也很坦率,谈到第二条准则时写道,他「曾为一家出版社工作,对方坚持要在合计行上方画一条双细线。但那不会是我的选择。」准则并非绝对,而是 要破就得有理由

hhline — 指定线条如何交叉

hhline 画出的横线和 \hline 相同,但可以 逐个字符地指定纵线在交叉处是被切断还是径直穿过。它是 David Carlisle 收录在 LaTeX tools 集合中的宏包,用 \usepackage{hhline} 载入。用原生的 \hline\hline 时,你无法选择纵线在双线处是断开还是贯穿。既然 booktabs 主张纵线和双线都不要用,hhline 的用武之地就只剩下 无法遵守这条建议的场合:规定了固定格式的投稿模板、需要复现的既有文档、会计表格等。反过来说,其余情况都用不到它。

\hhline{…} 的参数是一串很像列指定的记号。横线一侧:= 表示一列宽的双线,- 表示单线,~ 表示该列不画线。纵线一侧:|贯穿 横线的竖线,:被横线切断 的竖线,# 是夹在两条竖线之间的一段双线。此外还有用于构造双线拐角的 t(上半段)与 b(下半段),|t: 是左上角,:b| 是右下角。列指定中的 *{n}{…} 重复写法同样可用。注意,写出双纵线(||::)时横线会在该处断开;若想让横线不断地穿过去,请使用 #,或干脆不写纵线指定。

latex
\usepackage{hhline}

% the example from the package documentation: t/b build the corners,
% ~ leaves a column unruled, # is a double-rule segment between verticals
\begin{tabular}{||cc||c|c||}
  \hhline{|t:==:t:==:t|}
  a & b & c & d \\
  \hhline{|:==:|~|~||}
  1 & 2 & 3 & 4 \\
  \hhline{#==#~|=#}
  i & j & k & l \\
  \hhline{||--||--||}
  w & x & y & z \\
  \hhline{|b:==:b:==:b|}
\end{tabular}

第一行 |t:==:t:==:t| 构造双线的上半段和拐角,最后一行 |b:==:b:==:b| 构造下半段,于是外框的双线与纵线干净地衔接在一起。中间几行混用 ~(不画线)和 #(夹在竖线之间的双线段),逐行改变「哪里断、哪里通」。最后是一点技术提醒:\hline 是一条 TeX 的 \hrule,而 \hhline 的线由 许多小段拼成。它们在 DVI 中的位置是精确的,但某些查看器或驱动可能无法把这些小段严丝合缝地对齐。文档建议改用别的驱动,或稍微调大 \arrayrulewidth,让接缝不再显眼。

该用哪一种

结论几乎只有一条:普通表格一律以 booktabs 为默认。只用 \toprule\midrule\bottomrule 三条线,再在需要时配上 \cmidrule(lr){…},就能得到期刊上那种简洁易读的表格。遵守「不用纵线、不用双线」,画线便不再是需要反复斟酌的事。

  • booktabs — 几乎所有表格的首选:只用横线,粗细与留白已调好,出版品质。
  • 标准的 \hline / \cline / | — 优点是无需宏包;适合网格状表格和草稿。注意别画过头。
  • hhline — 只用于必须精确控制双线与纵线交叉方式的特殊场合。不是日常工具。
  • 每当想再加一条线时 — 先想想能不能改用 \addlinespace 以留白标示行的分组。空白比线更轻,却能完成同样的工作。

在罫线之前,列的排布与对齐决定了表格的骨架。&\\ 的基础,以及如何读懂 Extra alignment tabMisplaced \noalign,请看「tabular 基础」。包含 p{width}>{…} 的列指定细节,请看「列规格、对齐与宽度」。把 \multicolumn\cmidrule 组合成多层表头的做法,见「合并单元格」。把表格放进 table 环境并加标题,则属于「表格放置与样式」。