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 use … in 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 解决。
\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 | o̲ | 下加横线 |
\t | o͡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。
% 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——最好的办法是 url 或 hyperref 宏包提供的 \url{...}:它会自动处理其中的保留字符,以等宽字体排版,并在合适的位置断开长网址。
\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。分好之后,转义策略自然就定了。
% 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}总结起来,判断大致分三步。散文中的一两个符号,按表格所示转义。成块的代码或终端输出,交给 \verb 或 verbatim。URL,交给 \url{}。此后一旦出现症状——表格错开一列、行的后半消失、忽然报 ! Missing $ inserted.——就只在粘贴过的范围内检索,把每一个 &、%、_、# 逐一归入「散文」或「LaTeX 语法」。原因几乎总是就在那里。