> For the complete documentation index, see [llms.txt](https://overleaf-pro.ayaka.space/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://overleaf-pro.ayaka.space/latex/zh-cn/geng-duo-zhu-ti/19-how-tex-macros-actually-work-part-1.md).

# TeX 宏究竟是如何工作的：第1部分

[第1部分](/latex/zh-cn/geng-duo-zhu-ti/19-how-tex-macros-actually-work-part-1.md) [第2部分](/latex/zh-cn/geng-duo-zhu-ti/20-how-tex-macros-actually-work-part-2.md) [第3部分](/latex/zh-cn/geng-duo-zhu-ti/21-how-tex-macros-actually-work-part-3.md) [第4部分](/latex/zh-cn/geng-duo-zhu-ti/22-how-tex-macros-actually-work-part-4.md) [第5部分](/latex/zh-cn/geng-duo-zhu-ti/23-how-tex-macros-actually-work-part-5.md) [第6部分](/latex/zh-cn/geng-duo-zhu-ti/24-how-tex-macros-actually-work-part-6.md)

## 引言：本系列的目标

本系列文章有一个雄心勃勃的目标：解释 *如何* TeX 宏（例如 LaTeX 命令）究竟是如何工作的——在最基础的层面上，即在真正的 TeX 引擎软件内部。与其依赖 *仅仅* 一套旨在展示 TeX 各种特性、边界情况和行为的示例宏，我们将深入 TeX 内部，看看 *如何* 和 *为什么* 它的宏编程方法为何会以那样的方式工作。

为了实现我们的目标，我们需要先讨论一些相当底层的话题，而起初，这些内容看起来可能与文档排版的任务相距甚远。希望经过一次更深入的探讨后，你能建立起一个基础，从而更好地理解这些内容，最终为你节省大量时间，也许还能减少一些挫败感。

### TeX 编程语言：你有这种感觉吗？

把 TeX 编程语言描述为有些深奥并不过分，因为它确实如此——至少与当今大多数主流编程语言相比是这样。随着你开始踏上学习更多 TeX/LaTeX 的旅程，尤其是如果你想编写非平凡的宏，你会很快遇到诸如类别码、记号/记号化，以及命令或宏的“展开”等概念。如此密集的概念轰炸很可能相当陌生，也许会让你感到困惑，有时甚至会有点沮丧，因为你通往成功的道路并不总是有 TeX/LaTeX 那些几乎难以穿透的错误信息相助。

## 那么，我们从哪里开始？从类别码开始。

TeX 引擎属于一种称为以下的软件类别 [编译器](https://en.wikipedia.org/wiki/Compiler)：这类程序输入一个以 *源* 语言编写的文件，并 *编译* （转换）为一个以 *目标* 语言编写的输出文件。更具体地说，TeX 是一种 *文档编译器*。对于 TeX 引擎（编译器）来说，输入文件是用 TeX 排版语言编写的，而目标是一个以另一种“语言”编写的输出文件，例如 [DVI](https://en.wikipedia.org/wiki/Device_independent_file_format) 或 PDF——不过这里我们对“语言”的概念说得稍微宽松一些。

让我们更仔细地看看用于编写 TeX 文件的源文件，或者说输入“语言”。一个 `.tex` 文件归根结底是一长串字符（包括换行字符）：其中包含要进行排版的文本，并夹杂着 `\`, `}`, `$`, `[` 以及各种各样的字符，它们可以以看似近乎无限多的组合出现。任何不使用 TeX/LaTeX 的人看到一个典型的 `.tex` 文件时，完全可以原谅他们把它看成一团相当混乱的字符杂烩，几乎看不出任何文件结构。LaTeX 宏包当然在某种程度上为 .tex 输入文件“强加”了一些基本结构。然而，在 `\begin{document}` 和 `\end{document}` 这部分写些什么则取决于文档作者。如果你看看 `.tex` 使用 Knuth 原始 Plain TeX 宏包编写的文件，你会发现文档结构几乎完全不存在。

因此，总的来说，TeX 输入文件看起来可能相当无结构，看似是要排版的内容与指导这些内容排版的指令（命令）随意混合在一起。TeX 究竟是如何理解一个典型的 `.tex` 输入文件的：把输入进来的杂乱字符筛选成对排版引擎可执行的指令，以及要被排版的内容？

### 筛选这团杂乱：向类别码问好

任何对 TeX 一无所知的人类观察者，都可能看着一个 `.tex` 文件，并认出某些字符，例如 `$` 并知道那是货币符号，或者看到一个 `&` 并将其识别为一个与号。该观察者会为每个字符推断出一个 *含义* ——一种基于该字符在人类交流中所扮演角色而赋予的含义。此外，他们还可能看到诸如 `a`, `e`, `或` 并知道它们被归类为元音，而另一些如 `b`, `c` 或 `d` 则被归类为辅音。作为人类，我们在记忆中有一种内置的查找表，通过它我们为看到的每个字符赋予含义——这种含义基于该字符在我们能够交流的语言中所发挥的作用。

要处理一个 `.tex` 文件，TeX 软件也必须查看输入中的每一个字符，并且它也需要为 *含义* 它“看到”的每一个字符都分配一个。可是，TeX 只是一个处理文本的软件机器——这些文本以整数序列（字符码）的形式存储在输入文件中。作为一台机器，TeX 必须被编入相关数据，这些数据告诉它如何确定它正在“查看”的字符的含义，以及随后需要如何处理它。TeX 是如何做到这一点的？

答案就是 TeX 独有的那些概念之一： *类别码，* 它共有 16 个，范围从 0 到 15。就 TeX 而言，它预期在一个 `.tex` 文件中看到的每个字符都具有所谓的 *类别码* 预先分配给它的。TeX 软件内部有一种“查找表”，其中列出了 *当前* 分配给 TeX 可能在输入 .tex 文件中看到的每个字符的类别码。你应把 TeX 的类别码理解为给每个 *含义* TeX 必须检查（扫描）的输入流中的各个字符分配一个

要排版你的文档，TeX 引擎必须读取（扫描）每一个字符，但 TeX 直接关心的并不是实际字符（字符码）：字符的 *类别码* 在扫描输入时更为重要。一个字符的 *当前* 类别码决定了该字符的 *当前含义* of that character *在 TeX 读入它时*：该类别码决定 TeX 将如何对每个字符进行处理——我们稍后会解释为什么要说“当前类别码”和“当前含义”。正是通过类别码，TeX 才能筛选输入进来的杂乱字符，区分出用于排版的字符（内容）与构成待处理指令的字符——*命令插入边注* 这些指令需要 TeX 执行。

下表列出了这 16 个类别码：它们各自表示什么，以及通常被分配到每一类的字符示例。

| **类别码** | **说明**                           | **标准 LATEX/TEX**                 |
| ------- | -------------------------------- | -------------------------------- |
| 0       | 转义字符——告诉 TEX 开始查找命令              | `\`                              |
| 1       | 开始一个组                            | {                                |
| 2       | 结束一个组                            | }                                |
| 3       | 数学切换——进入/退出数学模式                  | $                                |
| 4       | 对齐制表符                            | &                                |
| 5       | 行尾                               | ASCII 码 13（`\r`)                 |
| 6       | 宏参数                              | #                                |
| 7       | 上标——用于排版数学： `$y=x^2$` $$y=x^2$$  | ˆ                                |
| 8       | 下标——用于排版数学： `$y=x_2$` $$y=x\_2$$ | \_                               |
| 9       | 被忽略的字符                           | ASCII 0 `<空>`                    |
| 10      | 空白符                              | ASCII 码 32（空格）和 9（制表符）           |
| 11      | 字母                               | A...Z，a...z，（以及成千上万的 Unicode 字符） |
| 12      | 其他                               | 0...9 以及 ,.;?" 和许多其他字符           |
| 13      | 活动字符                             | 用于创建单字符宏的特殊类别代码，例如 ˜             |
| 14      | 注释字符——忽略其后直到行尾的所有内容              | %                                |
| 15      | 无效字符，不允许出现在 .tex 输入文件中           | ASCII 码 127（`DEL`)               |

类别码的使用是 TeX 过滤输入字符流的核心机制，使其能够理解你的输入并确定：

* 构成待排版文本的字符；
* 界定应以数学公式方式排版的内容；
* 作为待处理或执行命令名称的字符序列；
* ……以及许多其他排版操作。

起初，你可能会认为每个字符的类别码（含义）是一种固定分配：不可改变，并且永久地嵌入 TeX 软件的内部基础之中，但事实并非如此。如前所述，TeX 维护着一个内部查找表，用于存储 *当前* 分配给每个字符的类别码——我们特意说 *当前分配* 是因为任何尚未读入的字符的类别码都可以通过使用一个称为 `\catcode`。

如果你主要关心的是使用 LaTeX 来“把事情做完”，那么你很可能并未直接接触过类别码，除非也许是通过你见过的错误信息。不过请放心，类别码是 TeX 引擎运作的核心组成部分：它使 LaTeX（以及 LaTeX 宏包）能够真正完成为你的文档排版的工作。

当你的 TeX 引擎启动时（“[引导启动](https://en.wikipedia.org/wiki/Bootstrapping)”）它会使用一套字符到类别码的默认分配，但通过 `\catcode` 命令，这些默认设置可以被核心 LaTeX 代码（宏）和/或你加载的 LaTeX 宏包所改变——或者实际上也可以被你自己的 TeX 代码或宏所改变。不过，随着时间推移和传统/使用习惯，某些被分配到特定类别码的字符已被公认为“标准”；如果你希望文档具有可移植性并能轻松与同事或其他用户共享，那么遵循这些标准当然是可取的。例如， `\` 字符被分配类别码 0，用于表示 TeX/LaTeX 命令的开始——参见 [上表](#tbl-0).

### 读取（扫描）输入

当 TeX 从你的输入文件中读取（扫描）下一个字符时，它做的第一件事就是查看该字符的类别码，所以让我们更仔细地看看 TeX 读取一行典型输入时会发生什么。

假设我们有一个包含如下文本的 .tex 文件 `Hello World \jobname` 位于某个段落中。如果我们查看 `.tex` 文件，使用一个 [十六进制编辑器](https://en.wikipedia.org/wiki/Hex_editor)，我们会看到字符序列 `Hello World \jobname` 在我们的 `.tex` 文件中只是一串整数，或者说是 *字符码*，如下方截图所示为十六进制序列：

`48, 65, 6C, 6C, 6F, 20, 57, 6F, 72, 6C, 64, 20, 5C, 6A, 6F, 62, 6E, 61, 6D, 65, 20`

![TeX 文件中的十六进制字符码](/files/dd3696c97d7286ee819358246e2aa2280412c539)

如果我们把十六进制（基数 16）转换为十进制（基数 10），字符码序列为：

![TeX 文件中的十进制字符码](/files/c0c59a77484764c5ab2cafa20270fcf42b89c2d8)

我们还知道，对于 TeX 来说，每个字符都有相应的类别码；因此，基于 [上表](#tbl-0) 我们知道，下面这些默认的类别码分配（大概）也在被使用：

![TeX 类别码](/files/dc13dc8a25a4aef221df5aa9bb25cc4507423be4)

因此，对 TeX 而言，输入文件中的每个字符都由 *两个* 两个数值表示——它的字符码和类别码：

![字符码及对应的 TeX 类别码](/files/25cae8574a461572f3de513df68478164a975df6)

此时我们只是在考虑 TeX 处理你的文件的最初阶段：扫描单个字符。那么 TeX 到底会如何处理这些字符码和类别码的配对呢？一旦 TeX 扫描了一个字符并查找出其对应的类别码，TeX 究竟如何利用这些信息来“过滤”输入的字符？

## 第2部分

在第 2 部分中，我们将更仔细地看看 TeX 是如何读取你的输入的：假装自己是 TeX 的“眼睛”，逐个字符地观察你的输入。

[第1部分](/latex/zh-cn/geng-duo-zhu-ti/19-how-tex-macros-actually-work-part-1.md) [第2部分](/latex/zh-cn/geng-duo-zhu-ti/20-how-tex-macros-actually-work-part-2.md) [第3部分](/latex/zh-cn/geng-duo-zhu-ti/21-how-tex-macros-actually-work-part-3.md) [第4部分](/latex/zh-cn/geng-duo-zhu-ti/22-how-tex-macros-actually-work-part-4.md) [第5部分](/latex/zh-cn/geng-duo-zhu-ti/23-how-tex-macros-actually-work-part-5.md) [第6部分](/latex/zh-cn/geng-duo-zhu-ti/24-how-tex-macros-actually-work-part-6.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://overleaf-pro.ayaka.space/latex/zh-cn/geng-duo-zhu-ti/19-how-tex-macros-actually-work-part-1.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
