特殊字符

LaTeX 保留的特殊字符正好十个——# $ % & ~ _ ^ \ { }——但这份名单并不是谁挑出来的。TeX 会给每个字符分配一个类别码(category code),普通字符是「字母(11)」或「其他(12)」。在可打印的 ASCII 中,恰好有十个字符的类别码不属于这两类,它们就是所谓的保留字符。所以「为什么是这十个」的答案既不是历史也不是偏好,而是 TeX 输入处理器的设定表本身。本页讲解如何逐一原样打印这十个字符、如何添加重音符号,以及在 UTF-8 已成默认的今日 LaTeX 中如何输入非 ASCII 文本。

十个保留字符与它们的类别码

所谓保留字符,是 LaTeX 用作自身语法组成部分的字符:敲下去不会被打印,而是触发一种行为。敲 %,该行剩余部分随即消失;敲 $,就切换进数学模式;敲 &,表格的一列就被分开。用类别码重述一遍:\ 是 0(开始一条命令),{ 是 1、} 是 2(分组的开与闭),$ 是 3(数学模式切换),& 是 4(对齐制表符),# 是 6(参数编号),^ 是 7(上标),_ 是 8(下标),~ 是 13(活动字符),% 是 14(注释)。而 a 是 11,1+ 是 12——只会被打印出来的普通字符。

把它们裸写下去会发生什么,因字符而异。最危险的是 %:它不会报任何错误,只是悄悄吃掉该行剩下的内容。& 给出 ! Misplaced alignment tab character &.;正文中的 _^ 给出 ! Missing $ inserted.} 给出 ! Too many }'s.\ 会把后面的字符读成命令名,于是 ! Undefined control sequence.。只有 # 的信息比较特别:它以 ! You can't use 开头,接着是 macro parameter character #' in horizontal mode.。而 ~ 是唯一既不报错也不警告的:它会被安静地排成一个不可断空格。

字符类别码作用裸写的结果
\0开始一条命令! Undefined control sequence.
{1开启分组或参数未闭合时会在文件末尾报错
}2结束分组或参数! Too many }'s.
$3开启与结束数学模式不成对时给出 ! Missing $ inserted.
&4表格与对齐环境的列分隔符! Misplaced alignment tab character &.
#6宏定义中的参数编号(#1#2、…)! You can't usein horizontal mode.
^7数学模式中的上标在正文中给出 ! Missing $ inserted.
_8数学模式中的下标在正文中给出 ! Missing $ inserted.
~13活动字符;不可断空格不报错;会静默变成不可断空格
%14注释;忽略该行剩余部分不报错;该行剩余部分悄悄消失

原样打印:七个只需转义

十个之中有七个很简单,只要在前面加一个反斜杠——\#\$\%\&\_\{\}。写 \$1.23 就会得到 $1.23。反斜杠的意思是「下一个字符是它自己,而不是某条命令的开头」。其机制也很朴素:在内核里,\%\&\# 就是用 \chardef 写成的——一条只说「打印这个位置上的字符」的定义。只有 \$\{\} 会检查当前是否处于数学模式并作出调整,所以同样的写法在公式内部也成立。

输入输出注意
\##前面加一个反斜杠即可
\$$在数学模式中同样可用
\%%用于百分号,如 50\%
\&&用于公司名、书名中的「and」
\__在默认编码下是用线条画出的,而非字形
\{{在数学模式中同样可用
\}}在数学模式中同样可用
\textasciitilde~\~{} 效果相同;裸写的 \~ 是重音命令
\textasciicircum^\^{} 效果相同;裸写的 \^ 是重音命令
\textbackslash\\\ 是换行命令,不会打印出反斜杠

剩下的三个:波浪号、脱字符与反斜杠

只有 ~^\ 这三个不能靠转义解决,原因很简单:它们「转义后的形式」已经另有含义。\~\^重音命令,会把波浪号或尖角放到后一个字母之上——\~n 得到 ñ,\^o 得到 ô。想单独得到符号本身,就交给它一个空参数,让它无处可放:那就是 \~{}\^{}。事实上,内核中 \textasciitilde\textasciicircum 的默认定义正是这两者。带名字的写法在稿件里更容易读出意图,因此推荐使用 \textasciitilde{}\textasciicircum{}

反斜杠本身用 \textbackslash 输出。不要用 \\——那不是两个反斜杠,而是换行命令,页面上什么也不会出现。有趣的是,默认正文字体里根本没有反斜杠这个字形。内核声明 \textbackslash 取自数学符号编码;果然,只含 \textbackslash 的单页 PDF 里嵌入的字体是数学符号字体 CMSY10。同样的道理,默认的 \_ 也不是字符,而是手工画出的一条细线:只含 \_ 的一页里,一个字体都不会被嵌入。加上 \usepackage[T1]{fontenc} 后,两者都会切换成正文字体中真正的字形。顺带一提,根本不属于保留字符的 <>|,在默认编码下也会变成 ¡ ¿ —,同样靠 fontenc 解决。

latex
\usepackage[T1]{fontenc}   % real glyphs for \, _, < > |

100\% \& \$5 cost \#1

A Windows path: C:\textbackslash Users\textbackslash doc

Standalone marks: \textasciitilde{} and \textasciicircum{}

添加重音符号

早期的 TeX 只能读取 7 位 ASCII。因此要排出 é,必须命令它「在 e 上放一个锐音符」,这些命令一直沿用至今。写法是「重音命令+字母」:\'e 是 é,\"o 是 ö,\^e 是 ê,\~n 是 ñ,\c{c} 是 ç,\v{s} 是 š,\r{a} 是 å。有一个坑值得一提:给 i 和 j 加重音时,字母上的点会碍事,所以要用无点的 \i\j,写成 \'{\i}

命令示例名称
\'é锐音符
\`è钝音符
\^ê扬抑符
\"ö分音符(变音符)
\~ñ波浪符
\=ō长音符
\.ż上加点
\uğ短音符
\vš短音符(háček)
\Hő双锐音符
\cç软音符
\rå上加圈
\d下加点
\b下加横线
\to͡o跨两字母的连结符

有些字母根本无法由重音拼出。德语的 eszett \ss(ß)、北欧的 \aa(å)与 \o(ø)、波兰语的 \l(ł),以及合字 \ae(æ)和 \oe(œ),本身就是独立的字母,因此各有独立的命令。大写形式只需把拼写改成大写:\AA\O\L\AE\OE。这些命令都属于标准 LaTeX,无需额外宏包。

直接以 UTF-8 输入

在当今的 LaTeX 中,café、Grüße、naïve 都可以直接打出来。自 2018 年 4 月的发行版起(LaTeX News 28 有一节题为「UTF-8: the new default input encoding」),UTF-8 成为内核默认的输入编码,因此不再需要 \usepackage[utf8]{inputenc}(写了也无害,旧文档照样能编译)。不过 \usepackage[T1]{fontenc} 仍值得加上:它把输出一侧切换为 8 位字体编码,带重音的单词才能正确断词,从 PDF 复制出的文字也不会损坏。用 XeLaTeX 或 LuaLaTeX 就更简单了——用 fontspec 指定 OpenType 字体,输入与输出从一开始就是 Unicode。

latex
% pdfLaTeX: UTF-8 input is the default; T1 fixes output and hyphenation
\usepackage[T1]{fontenc}

café, Gr\"u\ss e, na\"{\i}ve, \AA ngstr\"om, \oe uvre, a\~no

% XeLaTeX / LuaLaTeX: Unicode all the way through
% \usepackage{fontspec}
% \setmainfont{TeX Gyre Pagella}

特殊字符很多时:\verb\url

把满是符号的片段逐字转义既费力,又容易漏掉一个。这正是 verbatim 机制的用武之地。行内写 \verb|...|,用一个内容中不出现的字符(这里是 |)作为围栏;围栏之内,所有保留字符都失去特殊含义,文本以等宽字体排出。多行则用 verbatim 环境。有一个限制:\verb 不能出现在参数内部——写 \section{... \verb|x| ...} 会得到 ! LaTeX Error: \verb illegal in argument.。对于容易含有 ~#%_ 的字符串——尤其是 URL——最好的办法是 urlhyperref 宏包提供的 \url{...}:它会自动处理其中的保留字符,以等宽字体排版,并在合适的位置断开长网址。

latex
\usepackage{hyperref}   % provides \url as well

Use \verb|a_b^c & d%| to show specials literally.

\begin{verbatim}
foo_bar = 100% & #1   % every character printed as-is
\end{verbatim}

See \url{https://example.com/path?id=1#sec_2~ok}

粘贴的数据把表格弄坏时

在真实的稿件里,特殊字符往往不是自己敲进去的,而是从 CSV、网页、邮件、PDF 里进来的。表格是重灾区。由于 & 是列分隔符,公司名或论文标题中一个裸的 & 就会把列数搞乱,要么报出 ! Misplaced alignment tab character &.,要么悄悄吞掉该行的后半部分。最快的防守,是在粘贴之前先把素材分成三类:作为散文阅读的文字、原样展示的代码、作为链接处理的 URL。分好之后,转义策略自然就定了。

latex
% before pasting into a table, search the pasted region for & % _ #
\begin{tabular}{ll}
  Smith \& Wesson & company name \\
  95\%            & reported rate \\
\end{tabular}

% do not escape URLs by hand; hand them to \url
\url{https://example.com/report?id=95#table_1}

总结起来,判断大致分三步。散文中的一两个符号,按表格所示转义。成块的代码或终端输出,交给 \verbverbatimURL,交给 \url{}。此后一旦出现症状——表格错开一列、行的后半消失、忽然报 ! Missing $ inserted.——就只在粘贴过的范围内检索,把每一个 &%_# 逐一归入「散文」或「LaTeX 语法」。原因几乎总是就在那里。