标点与括号

对 LaTeX 来说,句点不是「打一个点」的命令,而是改变随后空白宽度的命令Dr. Smith 的空白是 3.33252pt,ends here. Next 的空白是 4.44336pt——键盘上敲的是同一个 .,在 10pt 文档里却差了 1.11084pt。而且判定的依据是句点前面的那个字符:小写字母表示句末,大写字母表示缩写。这一条规则,正是 NASA. Next 后面显得松散、Fig. 1 空得奇怪、e.g. 之后让人别扭的根源。本页讲清这套机制(\sfcode)、修正手段(\@\ ~\frenchspacing)、省略号 \ldots\dots 的分工、数字的千位分隔 \,,以及日文语境下句读与括号的取舍。

为什么句点后的空白会变宽——\sfcode 机制

因为每个字符都带有一个空格因子(space factor),当前一个字符留下的因子超过 2000 时,TeX 就会给随后的空白加上字体的「额外空白」\fontdimen7。在 10pt 的 article 里量:普通词间距 \fontdimen2 是 3.33252pt,额外空白是 1.11084pt,合计 4.44336pt——句末空白正好是词间距的 1.33 倍。在 \the\sfcode 后面接一个字符即可查看这些值,句点报出 3000。这个 3000 就是一切的起点。

字符默认 sfcode对随后空白的影响
. ? !3000句末空白;10pt 时 +1.11084pt
:2000略宽的空白;正好卡在阈值上
;1500仅伸缩余地略有变化
,1250同上;绝不算作句末
)0透明;原样传递前一个字符留下的值
A B C999低于 1000,会抵消随后 . 的 3000

Dr. Smith 紧、ends here. Next 宽——大写字母的 sfcode 为何是 999

因为大写字母的 sfcode 是 999,也就是略低于 1000。TeX 的规则是:当前空格因子低于 1000 时,即便后面的字符带有大于 1000 的值,空格因子也只会升到 1000 为止。所以 A 后面跟 .(3000),实际留下的值是 1000,句末的额外空白不会出现。反之小写 a 正好是 1000,随后 . 的 3000 就原样通过。实测:A. a 为 18.60657pt,而写明空白的 A.\ a 完全相同,也是 18.60657pt。另一边 a. a 是 17.21802pt,声明 \frenchspacing 后缩到 16.10718pt。这 1.11084pt 的差,正是句末的额外空白。

这个设计不过是把 TeX 诞生年代英语排版的习惯照搬进来:当时句与句之间比词与词之间空得更宽。而为了不把缩写里的句点——Dr.Ph.D.Mr.——误认成句末,TeX 采用了一条一行的经验法则:「以大写字母结尾的就是缩写」。既然是经验法则,两边都会失手。NASA. Next 明明是句末却排得紧,Fig. 1 明明是缩写却排得宽。接下来的两节分别解决这两种情况。

\@ 告诉 LaTeX「这里就是句末」——NASA\@. Next

\@ 的意思是「把紧随其后的标点当作句末」,它要放在句点之前。写成 He joined NASA\@. Then he left.,大写 A 留下的 999 就被抵消,. 的 3000 得以通过。实测:A. a 是 18.60657pt,而 A\@. a 是 19.7174pt——相差 1.11083pt,正好是句末的额外空白。放错位置写成 NASA.\@ Next 则毫无作用。请记住:\@ 放在句点前面。这是一年只用上几次的工具:只在句子以首字母缩写(NASAUSADNA)或以大写字母收尾的专名结束时才需要。

缩写之后用 \ ~——e.g.Fig.~1

反方向的误判——以小写字母结尾的缩写被当成句末——通过把空白写成命令来解决。\ (反斜杠加空格)意为「在此放置一个普通词间距」,完全不看空格因子。实测:Fig. 1 是 26.5213pt,Fig.\ 1 是 25.41046pt,窄了 1.11084pt,正是正确的词间距。e.g.i.e.etc.cf.vs.Fig.Eq.No. 都会掉进这个坑。

不过对于 Fig. 1 这类引用编号~(波浪号)比 \ 更合适。~ 表示「与词间距同宽、且此处不得断行的空白」;实测 Fig.~1Fig.\ 1 同为 25.41046pt。宽度相同,只是多了禁止断行。图表编号、公式编号、人名缩写若在行末被拆开会很难读,所以惯例写成 Fig.~\ref{fig:one}Eq.~\eqref{eq:main}D.~E.~Knuth。记住:引用编号用 ~,其他缩写用 \ ,就不会犹豫。

latex
% a sentence really ending on a capital
He joined NASA\@. Then he left.

% abbreviations that must NOT get a sentence space
See, e.g.\ Knuth (1984); cf.\ the appendix.

% reference numbers: same width, but unbreakable
Fig.~\ref{fig:one} and Eq.~\eqref{eq:main}, after D.~E.~Knuth.

\frenchspacing——几乎每份非英语文档都需要的一行

\frenchspacing一次性关闭句末额外空白的声明,在导言区写一行即可。它的内部简单得出奇:latex.ltx 中的定义只是把六个 sfcode 统一设为 1000,于是 .?!:;, 全部按普通字符对待(默认的 \nonfrenchspacing 则恢复为 3000/3000/3000/2000/1500/1250)。实测:a. a 从 17.21802pt 缩到 16.10718pt,而 A. a 稳定在 18.60657pt 不变——本来就没有额外空白的地方,自然不受影响。

名字虽如此,这种紧排并非法语独有。法语、德语、西班牙语、意大利语以及英语之外的大多数语言,都不会让句与句之间比词与词之间更宽。 就连现代英语排版也大体转向了单倍间距。给 babel 传入语言通常会自动切换,但自己声明 \frenchspacing 更可靠。顺带一提,LaTeX 自身在 verbatim 环境内部就用了它——代码里 . 后面被拉宽可不好。保留还是取消句末宽空白,是应当在开工前与合著者约定的方针;中途更改会让差异蔓延到每一页。

latex
\documentclass{article}
\usepackage[T1]{fontenc}
\frenchspacing          % . ? ! : ; , all behave like ordinary letters
\begin{document}
This ends here. Next sentence starts with an ordinary space.
\end{document}

省略号要用 \ldots 而不是 ...——以及 \ldots\dots 的区别

连打三个句点的 ... 宽 8.3313pt,\ldots 宽 13.33008pt——相差 1.6 倍... 的点挤成一团,看不出「省略」的意思;\ldots 则在点之间留出正确的空隙。所以正文中的省略号一定要用 \ldots\dots。那这两者有何区别?在纯 LaTeX 里没有区别latex.ltx 定义了 \dots 之后写下 \let\ldots\dots,二者字面上就是同一个命令;实测在文本模式下都是 13.33008pt,与 \textellipsis 完全一致。

差别只在加载了 amsmath、而且身处数学模式时才出现。amsmath 把二者分别重新定义:\ldots 恒为低位点,\dots 则成为向前预读、按上下文选择点的高度的命令。若是逗号序列就取低点(\dotsc);夹在二元运算符或关系符之间就取居中点(\dotsb,即 \cdots);积分用 \dotsi,乘法用 \dotsm。实际编译后用 pdftotext 抽取:$a+\dots+b$ 得到「a + · · · + b」,$a+\ldots+b$ 得到「a + . . . + b」——确实是不同的字符。不过在文本模式下两者都落到 \textellipsis,所以正文里不必纠结。

命令输出何时使用
\ldots…(贴基线)正文中的省略号;数学中恒为低位点
\dots依上下文amsmath 下按上下文选高度;数学中的首选
\cdots⋯(居中)夹在运算符之间,如 a+\cdots+b
\vdots⋮(竖排)在矩阵或方程组中省略若干行
\ddots⋱(斜向)沿矩阵对角线的省略
latex
\usepackage{amsmath}
% ...
He hesitated\ldots{} then went on.
\[
  x_1, x_2, \dots, x_n \qquad a_1 + a_2 + \dots + a_n
\]
\[
  \begin{pmatrix} a & \cdots & b \\ \vdots & \ddots & \vdots \end{pmatrix}
\]

数字分位用 \,——用 1.6663pt 造出「1 000」

\, 是插入细空格(thin space)的命令,宽 1.6663pt,即 1 em 的六分之一。量一下 1\,000 得 21.66142pt,1000 是 19.99512pt——差值正好 1.6663pt。由于用逗号还是用点作千位分隔在各国正好相反(英语的 1,000 在德语里是 1.000),国际惯例长期推荐改用细空格分隔\, 正是为此而设的空白。单位之前也用同一个命令,写作 5\,\mathrm{kg}

不过随着位数增加,手工撒 \, 就不划算了。若稿件中数值与单位密集,交给 siunitx,写 \num{1000000}\qty{5}{\kilogram} 更好——分位、单位前的空白,乃至负号的处理都会保持一致。请把 \, 看作「正文中偶然出现的某一个数值」的工具。

日文用「、。」还是「,.」——jsclasses 并没有切换选项

出什么符号取决于你在源码里敲什么,类选项无法切换。 jsarticle、jsbook 等 jsclasses 并没有把标点改成 ,.的选项。想用逗号句点,最直接的办法就是在正文里直接输入 ,.。这一流派本身源自规定标准文件样式的 JIS Z 8301——它在横排中使用「,」「.」——至今在数学书与理工科论文中仍很常见。另一方面,2022 年(令和 4 年)文化审议会建议《公用文写作方针》确定:读点原则上用「、」,横排时可视情况使用「,」。遵循投稿规定与所在领域的惯例,并在同一份文档内保持统一——判断标准仅此而已。

latex
% comma-period style: just type the marks you want
\documentclass{ltjsarticle}
\begin{document}
本稿では,記法を次のように定める.まず,集合 $A$ を……
\end{document}

日文括号 「」『』()——各自用在哪里

对话、引语与衬托词句用直角引号 「」;书名、作品名,以及嵌在 「」 内层的引用用双直角引号 『』。补充说明与读音用全角()。日文中请使用全角括号,不要混入西文半角 ():半角括号前后的留白规则不同,混一个进去,字面就会在行里明显浮起来。反过来,在西文(拉丁)引文内部则使用半角 ( )

括号名称典型用途
「 」直角引号对话、引语、衬托词句;日文的首选引号
『 』双直角引号作品名,以及嵌在「」内部
( )全角圆括号补充、注记、注音
[ ]全角方括号编者补充与标明的省略
【 】方头括号需要强烈突出的标题与条目名
〔 〕六角括号注记与补充;方括号的替代

至于日文约物究竟如何获得留白——JFM 中记录的字宽、连续右括号时的收紧、\inhibitxspcode、和欧文之间自动插入的 \xkanjiskip、行首行末禁则及其罚分——由「日语排版细节」一页负责。这里要记住的实务要点很简单:不要自己在约物前后敲空格。 敲了就会与引擎已经准备好的留白叠加,空出不自然的口子。

提交前搜索源码——一次性铲平标点的漂移

标点的偏差不会引发编译错误,只会在 PDF 上表现为一种别扭。所以最后做一次机械化的源码搜索最为可靠。合作写作时必然会出现这样的漂移:某一章按 \frenchspacing 的前提写、某一章 e.g. 后面留着裸空格、某一章在日文里混入半角括号。请对以下四项执行 grep

  • 缩写后的裸空格。 搜索 e.g.i.e.cf.etc.Fig. 后面的空白,改为 \ ~(若已用 \frenchspacing 则无需)。
  • 以大写结尾的句末。 搜索首字母缩写加 .NASA.USA.DNA.),凡真是句末就插入 \@
  • 省略号。 搜索连续三个句点 ...,改成 \ldots;在数学模式里交给 amsmath\dots
  • 日文中的半角括号。 搜索夹在日文行里的 ( ),改为全角 ( )。顺带检查约物前后是否被手工敲了空格。
terminal
# abbreviation followed by a bare space
grep -nE "(e\.g\.|i\.e\.|cf\.|etc\.|Fig\.) " *.tex

# a sentence that may end on an acronym
grep -nE "[A-Z]{2,}\." *.tex

# three typed periods instead of \ldots
grep -n "\.\.\." *.tex