你在 LaTeX 源文件里敲下的空格不是字符,而是一个尺寸。在 10 磅的 Computer Modern 中,词与词之间的空格正好是 3.33252 pt——量一量就会发现,~ 和 \ (反斜杠加空格)也是 3.33252 pt。而在另一些位置,无论敲多少个空格,一个也留不下来:\LaTeX is 排出来就是「LaTeXis」。本页讲 LaTeX 自动插入的各种空白的实测宽度、把消失的空格找回来的命令,以及自己动手加空白时 ~、\hspace、\quad 该怎么选。
LaTeX 的空白到底有多少磅
在 10 磅的 article 中载入 T1 的 Computer Modern 实测,词间空白是 3.33252 pt,可伸 1.66626 pt,可缩 1.11084 pt。这三个数字属于字体本身,TeX 里分别叫 \fontdimen2、\fontdimen3、\fontdimen4。也就是说 LaTeX 的空白不是「一个宽度」,而是「能伸能缩的宽度」——TeX 术语称为 胶(glue)。两端对齐的行之所以能齐平,正是因为这份伸缩量被逐行分配;若空白只是个字符,这件事根本无从谈起。
相反,自己放置的空白里有几个是完全不伸缩的。\,(细空格)是 1.6663 pt,\enspace 是 4.99878 pt,\quad 是 9.99756 pt,\qquad 是 19.99512 pt。这四个都以 em 定义:\quad 恰为 1 em,\qquad 为 2 em,\, 为六分之一 em,\enspace 为半 em。因为 em 随字号变化,同一个 \quad 在 \large 里会长到 11.74713 pt,而 \hspace{10pt} 无论写在哪里都是 10 pt。要跟着正文的节奏就用 em 系,要对齐图表的尺寸就用绝对长度——选择的要点尽在于此。
| 命令 | 10pt 正文实测 | 定义与性质 |
|---|---|---|
(ordinary blank) | 3.33252 pt | \fontdimen2;带 1.66626 pt 伸、1.11084 pt 缩的胶 |
\ | 3.33252 pt | 控制空格;与词间同宽,但不受句末判定影响 |
~ | 3.33252 pt | 前置 \nobreak 的 \ ;同宽,但该处不换行 |
\, | 1.6663 pt | 六分之一 em 的细空格;等同 \thinspace;不伸缩 |
\negthinspace | −1.66631 pt | \, 的反向;用于收紧 |
\enspace | 4.99878 pt | 半个 em;常用于列表标签周围 |
\quad | 9.99756 pt | 恰为 1 em;在 \large 里长到 11.74713 pt |
\qquad | 19.99512 pt | 2 em;用于分隔公式或例子 |
\hspace | 按指定值 | \hspace{10pt} 恒为 10 pt;但落到行首会被丢弃 |
\hspace* | 按指定值 | 行首也不消失的版本;该处亦不换行 |
\LaTeX\ is 与 \LaTeX{} is 并不同宽
紧跟在纯字母命令后面的空白,会被当作命令名结束的信号消耗掉,一磅也到不了纸面。实测 \LaTeX is 宽 32.37791 pt,\LaTeX\ is 宽 35.71043 pt——差值恰为 3.33252 pt,正好一个词间空白。搜索「latex 空白消失」的人首先撞上的就是这个症状,用 pdftotext 从 PDF 里抽文字,真的会得到连在一起的 LaTeXis。多敲几个空格,或者折到下一行,结果都一样。
修法有三种,\LaTeX\ is、\LaTeX{} is、{\LaTeX} is 实测都是 35.71043 pt。但「三者相同」是有条件的。 一旦前面是句点,结果就分道扬镳:etc.\ Next 实测 39.99023 pt,而 etc.{} Next 是 41.10107 pt,差 1.11084 pt——恰是句末追加的那份额外宽度(\fontdimen7)。原因在于 \ 是一种永远不接收这份额外量的空白,而写在 {} 后面的普通空格会被当作句末空白撑开。所以 \ 不只是「把消失的空白找回来」,同时还取消了句末待遇——这正是它用来修 Fig.\ 5 这类缩写的原因。句末判定本身的机制——\sfcode、\@、\frenchspacing——见「标点符号与括号」页。
% widths measured with \sbox0{...}\typeout{\the\wd0}
% pdfLaTeX, TeX Live 2024, 10pt article, T1 Computer Modern
\LaTeX is % 32.37791pt -> "LaTeXis" (the blank is eaten)
\LaTeX\ is % 35.71043pt
\LaTeX{} is % 35.71043pt
{\LaTeX} is % 35.71043pt
% ...but after a period the two fixes are NOT the same:
etc.\ Next % 39.99023pt (control space: no sentence space)
etc.{} Next % 41.10107pt (+1.11084pt = \fontdimen7)xspace 为什么既是解药也是陷阱
xspace 宏包让你在自定义命令末尾写 \xspace,于是「若下一个字符不是标点就补一个空白」,省得每次都写 \LaTeX{}。但这个判断不过是与例外表比对而已。TeX Live 2024 附带的 xspace.sty(由 LaTeX Project 维护,属 tools 套件)中的例外包括逗号、句点、撇号、斜杠、问号、分号、冒号、叹号、波浪号、连字符和右圆括号,另有 \ 、\/、花括号、\space、\footnote、\footnotemark。凡不在表上的东西之前,\xspace 都会插入空白。
右方括号 ] 不在表上。于是定义 \newcommand{\eg}{e.g.\xspace} 后写 [\eg],输出会是 [e.g. ]——右括号前多出一个空白。更糟的是,因为前一个字符是句点,插入的是句末宽度:实测 22.2168 pt,而不带 \xspace 是 17.77344 pt,相差 4.44336 pt。同样的事发生在 \eg\%(得到 e.g. %)、\eg\@. 以及直双引号之前。反方向的失败同样存在:为了保险写成 \eg{},花括号恰好命中例外,于是一点空白也不加,\eg{}x 排出来是 e.g.x。
\usepackage{xspace}
\newcommand{\eg}{e.g.\xspace}
\eg foo % "e.g. foo" correct
\eg, foo % "e.g., foo" correct (comma is an exception)
[\eg] % "[e.g. ]" WRONG -- ] is not an exception
\eg\% % "e.g. %" WRONG
\eg{}x % "e.g.x" WRONG -- the brace matches an exception
% You can patch the list, but every new case needs another patch:
\xspaceaddexceptions{]}例外可以事后追加,比如 \xspaceaddexceptions{]},但每加一次都得再数一遍「接下来还漏了什么」。所以老练的作者往往索性不用 \xspace:老实定义 \newcommand{\eg}{e.g.},在调用处写 \eg{} foo;或者干脆不做缩写命令,直接敲 e.g.。\xspace 真正划算的场合,只有调用次数多、而且能保证后面只会跟正文词语的情形——比如产品名或项目名的宏。
~ 在哪些地方是必须的——不换行的空白
~ 是一个「宽度与词间空白相同,但不允许在此换行」的空白。它背后并没有什么特殊的空白:latex.ltx 中的定义是 \DeclareRobustCommand{\nobreakspace}{\leavevmode\nobreak\ }——就是前面加了 \nobreak 的 \ 。所以实测 x~y 与 x\ y 一样是 3.33252 pt 的空隙,并且和 \ 一样不接收句末的额外量(x.~y 为 16.6626 pt,而普通的 x. y 为 17.77344 pt)。人们搜索「latex 波浪号 换行」时想弄清的行为,全部落在这一个 \nobreak 上。
效果在窄栏里最容易看清。把「We show in Figure 5 that it holds.」排进 3 cm 宽的 parbox,行末会停在 Figure,5 被推到下一行开头。改成 Figure~5,TeX 就宁可把 Fig-ure 断开加连字符,也不拆散这两者。所以 ~ 不只是「禁止在此断行」,它还让 TeX 去寻找另一套断行方案。真正称得上必须的位置,是那些数字、符号或缩写一旦孤零零地立在行首就读不通的地方——Figure~5、Section~2、Eq.~(3)、Chapter~12、Dr.~Smith、Knuth~(1984)、首字母缩写 D.~E. Knuth,以及 10~kg 这样的数值与单位。交叉引用和文献引用中,波浪号应放在 \ref 和 \cite 之前:Figure~\ref{fig:main}、see~\cite{knuth1984}。
% in a 3cm column, the plain space breaks the reference in half:
We show in Figure 5 that it holds. % -> "...in Figure / 5 that..."
We show in Figure~5 that it holds. % -> "...in Fig-/ure 5 that..."
See Section~\ref{sec:method} and Figure~\ref{fig:main}.
As shown by Knuth~\cite{texbook}, a mass of 10~kg suffices.
D.~E. Knuth, Dr.~Smith, Eq.~(3), Chapter~12.有两点要留意。其一,~ 只是不换行,并不阻止连字符断词。上例中 Fig-ure 被断开正是如此;若要保住词本身,应当用 \mbox{...}。其二,~ 是 LaTeX 设为活动字符的符号,因此在 verbatim 之外不能直接敲它来得到 URL 或文件路径里的波浪号。要在正文中打印波浪号字符,请用 \textasciitilde。
\hspace 与 \hspace* 的区别——行首会不会消失
在行中间,两者毫无差别。差别出现在恰好在那个空隙处换行的时候。TeX 有一条规则:换行后紧接着的胶会被丢弃,所以若断行落在那里,\hspace{1.5cm} 会整个消失。实测:在 3 cm 宽的栏里排 alpha beta gamma\hspace{1.5cm}epsilon zeta,TeX 就在那个空隙处换行,1.5 cm 无影无踪。改成 \hspace*{1.5cm},那里就不再换行,改为把 epsilon 断开加连字符,1.5 cm 的空隙留在了纸面上。
实务中,若这份空隙本身是版面的一部分,就选带星号的:扉页的签名栏、标题前后固定的间距,或与 \hfill 配合把元素推向左右两侧的排布。反之,若只是「为了好读稍微空一点」,不带星号就够了,行末自动消失反倒方便。负值也可用——\hspace{-1em} 实测正好收紧一个 em。
\ignorespaces 与 \unskip——清除环境前后冒出来的空白
\ignorespaces 是跳过其后所有空白的命令,\unskip 则是撤销紧邻其前的一段胶的命令。前者吃掉输入,后者削减输出——这样记就不会混。实测:a b 是 13.8855 pt,a \unskip b 是 10.55298 pt,恰好少了一个词间空白。a\ignorespaces b(两个空格)同样是 10.55298 pt。
它们的用武之地几乎都在自定义环境的前后。定义 \newenvironment{note}{\textbf{Note:}}{\textbf{.}},写 \begin{note} yes \end{note} tail,\begin{note} 之后与 \end{note} 之前敲的空格都会跑到纸面上,整体宽 70.92018 pt。在开始代码末尾加 \ignorespaces、在结束代码开头加 \unskip,就变成 63.1443 pt——与手写 \textbf{Note:}yes\textbf{.} tail 连一个 scaled point 都不差。从 PDF 抽出的文字也从 Note: yes . tail 变成 Note:yes. tail。把这两个命令写进环境定义,在多人合写的稿子里尤其见效——不同作者在 \begin 后要不要换行各不相同,而输出不再摇摆。
% leaks a space on both sides -- measured 70.92018pt
\newenvironment{naive}{\textbf{Note:}}{\textbf{.}}
% tight on both sides -- measured 63.1443pt, identical to hand-set text
\newenvironment{tidy}{\textbf{Note:}\ignorespaces}{\unskip\textbf{.}}
\begin{tidy} yes \end{tidy} tail
% \ignorespacesafterend also swallows the blank AFTER \end{...};
% use it only when the environment must butt against what follows.- 先把波浪号写进去。
Figure~\ref{...}、Section~2、10~kg、Dr.~Smith落笔时就带上,事后再回头找的代价很高。 - 记住
\身兼两职:既把丢失的空白找回来,又取消句末待遇。Fig.\ 5之所以奏效,靠的是后一职。 - 默认不用
\xspace。 若一定要用,交稿前至少检查右方括号、\%和引号之前的效果。 - 属于版面的空隙用
\hspace*,只为好读的空隙用\hspace。 只有带星号的那个在行首不会消失。 - 自定义环境从第一天就写上
\ignorespaces与\unskip。 定义当天就加好,稿子再长也不会冒出多余空白。