语法规则

写下 \LaTeX is great,输出的却是「LaTeXis great」。一个空格不见了,而且没有报出任何错误。这不是缺陷,而是 TeX 按照规则故意把那个空格丢掉了。LaTeX 的所有语法规则——命令名在哪里结束、空格与换行如何被折叠、% 注释究竟删掉了什么——都来自同一件事:TeX 是怎样一行一行读你的源文件的。本页跟着这个读取过程走一遍,从机制而不是症状入手,处理掉初学者必踩的三颗地雷:消失的空格、多出来的空格,以及停不下来的字体设置

命令在哪里结束:控制词与控制符号

命令的名称在第一个不是字母的字符处结束。源文件的大部分是照原样排出的正文,但只要出现反斜杠 \(在某些日文环境中会显示为日元符号 ¥),TeX 就切换读法,开始读取一个名称。取名的方式只有两种:\ 后面只跟英文字母(A–Z, a–z)的叫控制词(control word)\ 后面跟一个非字母字符的叫控制符号(control symbol)\section\LaTeX\newpage 属于前者,\$\%\&\ (反斜杠加空格)属于后者。

这个区分不是文字游戏:写错时的症状完全不同。控制词的名称只要后面还是字母就会继续延伸,所以 \LaTeXlogo 不会被拆成 \LaTeXlogo,而是读成一个名称 \LaTeXlogo;这个命令并不存在,编译会以 ! Undefined control sequence. 停下。若想在徽标后面接字母,就必须用非字母的东西把名称切断:空格、符号或 {}。控制符号则在一个字符处就已确定,无法延伸,因此 \$5 会老老实实读成美元符号后跟 5。正是这种「只有一个字符」的性质,让 \$\% 在后面紧跟数字或符号时依然安全。

latex
% A control word is letters only; its name stops at the first non-letter.
\LaTeX        % the logo
\LaTeXlogo    % ! Undefined control sequence. -- read as ONE name

% A control symbol is exactly one non-letter after the backslash.
\$ \% \& \_   % the literal characters $ % & _
\,            % a thin space

为什么 \LaTeX is 会输出「LaTeXis」

紧跟在控制词后面的空白,无论有几个都会被跳过。 这些空白只是作为「名称到此结束」的信号被消耗掉,一点也不会留到输出里。所以 \LaTeX is great 会变成「LaTeXis great」。容易被忽略的是:被吃掉的不只是空格,制表符也一样,换行同样如此。为了让源码好读而在 \LaTeX 处折行、把 is great 写到下一行,结果并不会改变。也就是说,「我明明打了空格」最不管用的地方,正是命令的紧后面。

修正方法有三种,结果都是「LaTeX is great」。(1) 空花括号 \LaTeX{} is——{} 不是字母,名称到此结束,随后的空格作为普通词间空格保留下来。(2) 控制空格 \LaTeX\ is——\ (反斜杠加空格)本身就是「在此放一个词间空格」的命令。(3) 把命令整个包在花括号里 {\LaTeX} is。实用上以 (1) 最稳妥:无论后面是空格还是符号都一样有效,日后改写句子也不会失效。由于这种空格消失在自定义命令中最为高发,tools 宏包集提供了 xspace 宏包(David Carlisle 等编写)。把 \xspace 放在定义末尾,只有下一个字符不是标点时才会补上空格。

latex
\LaTeX is great        % -> LaTeXis great   (the space is eaten)

\LaTeX{} is great      % -> LaTeX is great
\LaTeX\ is great       % -> LaTeX is great
{\LaTeX} is great      % -> LaTeX is great

% In your own macros, let xspace decide:
\usepackage{xspace}
\newcommand{\myTeX}{\LaTeX\xspace}
\myTeX is great, and \myTeX, and \myTeX.  % space added, then not, then not

相对地,控制符号不会吃掉后面的空白:写 \$ 5,美元符号与 5 之间的词间空格会保留下来。由此也能看出 \ 的另一个用途。LaTeX 默认会在句末句点后留出稍宽的空白,于是仅表示缩写的句点——Prof. Smithet al. (1993)——也会被一起拉宽。写成 Prof.\ Smithet al.\ (1993),就是在告诉 LaTeX「这里不是句末」,从而保持词间距一致。反过来,若需要「此处不要断行」,就用连结空格 ~Fig.~1Chapter~12Donald~E. Knuth 中的 ~ 宽度与普通词间空格相同,但绝不会在那里断行。要做精细调整还有 \,(细空格):数学模式下是 \thinmuskip,即 3 mu,也就是六分之一 em;正文中是 0.16667 em——两者宽度相同。

花括号的两项工作:传递参数与划定范围

花括号 { } 包住必选参数,方括号 [ ] 包住可选参数(选项)。顺序一般是 \command[options]{required}\section{Introduction} 中的 {Introduction} 是必需的标题文字;\documentclass[12pt]{article}[12pt] 是选项,{article} 是必需的类名。有些场合单字符参数可以省略括号,但保留括号更安全。日后一旦加词,没有括号的参数会以两种方式之一出错:吞掉相邻字符,或被相邻字符吞掉。

花括号还有第二项工作:它会开启一个分组。改变字体或字号的声明式命令——\bfseries(粗体)、\itshape(斜体)、\large(变大)——完全不带参数,只是把「从这里开始」的一切都改掉。放任不管会让文档余下部分一直是粗体,原因正在于此。用花括号包住,效果就被围在那个分组之内;一越过右花括号 },此前的设置立刻恢复。{\bfseries bold here} back to normal 与带参数的 \textbf{bold here} back to normal 结果相同,但前者因为划出了一块区域,正适合需要让多个声明同时生效的场合。

latex
\section{Introduction}             % {...} = mandatory argument
\documentclass[12pt]{article}     % [...] = optional, {...} = mandatory

{\bfseries bold here} back to normal   % braces fence the declaration in
\textbf{bold here} back to normal      % the argument form does the same

{\large\bfseries several at once} back to normal

这种「划定范围」的思路,与 \begin{...}\end{...} 环境完全是同一回事。环境在开始时开启一个分组、在结束时关闭它,因此内部的字体变更不会漏到外面。花括号与环境只是规模不同的同一件工具;细节由环境那一页接手。

空格、换行与空行是如何被折叠的

连续的空白无论有多少个都会合并成一个词间空格,源码中的单个换行同样变成一个词间空格。 词与词之间放一个空格还是十个,或者折到下一行,输出都一样。这非但不是不便,反而是整个设计的枢纽:正因为断行位置完全由 LaTeX 决定,两端对齐和连字才有可能实现。于是源码这边可以按任意宽度换行,版本控制的差异也能保持在每行一句、便于阅读的状态。

那么段落如何切换?答案是空行。留出一行空白,那里就是段落的分界;连续两行或更多空行,仍然只算一次分界。这里有一个容易忽略的细节:只由空格组成的行同样被当作空行。TeX 在读取每一行之前会丢掉行尾的空白,所以只有空格的行会变成什么都没有的行。当段落在不该断的地方断开,或在该断的地方不断时,请怀疑正是这样一行——在编辑器里它和真正的空行看不出区别。段落本身的处理(\par\parindent)由换行与段落那一页负责。

latex
These     extra   spaces    collapse into one,
and this newline becomes a single space too.

A blank line -- and only a blank line -- starts a new paragraph.


Two blank lines do exactly what one does.

% 不只删掉本行,还会删掉行末的换行

% 到该行结尾都是注释,排版时会被完全忽略——而且消失的不只是这一行的剩余部分,还包括行末的换行本身。 留备忘、临时禁用命令是它显而易见的用途,但正是这个被吞掉的换行造就了 % 的另一大主力用法。如上一节所示,源码中的换行会变成一个空格。于是当你只是想把一个长词或长命令在源码里折行时,折行本身就会塞进一个你并不想要的空格。在行尾放一个 %,这个空格就不会出现,下一行会严丝合缝地接上。更妙的是,% 之后开始的那一行,行首的空白也会被跳过,所以继续行想缩进多少都可以。源码保持整齐,输出仍然是一个词。

latex
Visible text % from here to the end of the line is dropped, newline included

Anti%
        disestablishmentarianism   % -> one word: the indent is dropped too

\newcommand{\courseTitle}{%
  Advanced Topics in \LaTeX%
}                                  % no stray space at either seam

The rate was 100\% last year.      % a literal percent sign is \%

这里要提醒一点:% 本身是特殊字符,因此要输出字面上的百分号,应写作 \%。一旦忘记,该行剩下的部分会整个消失——没有错误也没有警告,半句话就这样从 PDF 里不见了,这在症状里属于最难缠的一类。另外,\% 是控制符号,不会吃掉后面的空白。其他特殊字符(#$&_{}~^\)的转义方法整理在特殊字符那一页。

若要一次禁用多行,除了给每行加 % 之外还有两种办法。一是用 verbatim 宏包(或 comment 宏包)提供的 comment 环境 包住整块:\begin{comment} … \end{comment};二是用 TeX 的条件判断 \iffalse … \fi 包住。后者不需要宏包、上手快,但 TeX 仍会读取其中内容,因此被搁置的文本里若有不配对的 \if,就会在那里出问题。要把整份草稿封存起来,comment 环境更省心。

latex
\usepackage{verbatim}   % in the preamble

\begin{comment}
This whole block is ignored.
\end{comment}

\iffalse
Draft text, parked with no package needed.
\fi

空格消失或多出来时的检查顺序

先按症状是否伴随错误分成两路。! Undefined control sequence. 那样中断的问题,要怀疑命令名在哪里结束;而没有错误也没有警告、只是输出不对的问题,要怀疑空白和注释——这就是顺序。后一类不会给出有用的行号,只能按类别缩小范围。从报错行稍前开始,检查命令名是否吞掉了后面的字母、{...}[...] 是否配平;接着检查行尾的 % 是否删掉了需要的空格,或是本该输出字面百分号的地方是否漏写了 \%

你写的症状修正
\LaTeXlogo! Undefined control sequence. 中断用非字母切断名称,如 \LaTeX{}logo
\LaTeX is词粘在一起变成「LaTeXis」\LaTeX{} is\LaTeX\ is;自定义命令用 \xspace
\bfseries其后一直保持粗体{\bfseries ...} 围住,或改用 \textbf{...}
100%该行剩余部分悄无声息地消失字面百分号必须写成 100\%
(a line of spaces)段落在意料之外的地方断开只有空格的行也是空行;在编辑器中显示不可见字符

最后,与其背诵规则,不如直接确定不易出错的写法。命令后面接字母时用 {} 切断;有意义的单位用 {...} 围起来;长定义不要塞进一行,用行尾 % 折行;不要让注释掉的旧稿在 .tex 里越积越多——想比较的版本交给 Git 之类的历史工具,只保留现在要排的内容,这样错误行号才会与真实稿件对应,调试也更快。这四条习惯能挡掉初期的大部分磕绊。