> 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/shen-ru-wen-zhang/54-what-is-a-tex-token-list.md).

# 什么是 TeX 标记列表

## 那么，究竟什么是“TeX 记号列表”？

在一个 [上一篇文章](/latex/zh-cn/shen-ru-wen-zhang/53-what-is-a-tex-token.md)——也是这一 [关于底层 TeX 技术细节的系列文章](/latex/zh-cn/shen-ru-wen-zhang/01-a-new-series-of-articles-tex-tokens-and-related-concepts-but-why-and-how.md)——我们探讨了 TeX 扫描你的 `.tex` 文件以生成新记号的过程：我们考察了一个 TeX 记号的基本性质，以及 TeX 如何创建它们（见 [什么是“TeX 记号”？](/latex/zh-cn/shen-ru-wen-zhang/53-what-is-a-tex-token.md)).

在这篇后续文章中，我们来看看 *记号列表*s：它们是什么，以及 TeX 引擎如何创建/使用它们。理解记号列表可能有点棘手，因为它们存储在 TeX 内部很深处：这些细节对用户是隐藏的——不过，今天如果你使用 LuaTeX 进行更高级的编程，这种情况并不总是如此。但现在，你可以先把记号列表想成 TeX 用来存储一系列整数值的方式，其中每个整数都是从 TeX 从你的输入文件中读取的某个字符或命令派生出来的记号。

记号列表在 TeX 的内部运作中扮演着关键角色，而且常常以一些出人意料的方式发挥作用，例如像以下命令的内部运作： `\\uppercase` 和 `\lowercase`。记号列表一个特别重要的用途是存储和执行宏，这一主题我们将在本系列未来的一篇文章中详细探讨。

### TeX 从文件和记号列表中获取输入

TeX 引擎有三种输入来源——其中两种你可能已经知道：

* 存储在磁盘上的物理文本文件；
* 用户在终端（命令行）中输入的文本；

但它还有第三种读取/获取输入的方式：记号列表！

事实上，记号列表是 TeX 用来在内部存储数据的一种设施，也是 TeX 操作的一部分。由于 TeX 的记号列表会充当先前创建的记号的“存储设施”，因此 TeX 将它们重新用作另一种输入来源是很合理的。当需要从某个特定记号列表中获取下一次输入时（或者 TeX 被指示这样做时），TeX 会暂时停止从物理文件中读取输入（即创建 *新记号*），并切换为从 *已有记号*中获取输入：也就是存放该记号列表的内存位置。显然，对于记号列表来说，扫描 + 生成记号的过程已经发生过了，所以 TeX 只需要查看列表中的每个记号，并决定对每个记号做什么。

以一个快速示例来说，底层（TeX 原始命令） `\\toks` 命令可以让你创建一个记号列表，TeX 会将其保存在内存中以供日后重用：

```latex
\toks100={Hello}
```

要取回这些记号（即告诉 TeX 将它们视为下一输入源），你可以发出如下命令：

```latex
\the\toks100
```

这会使 TeX 从你的输入文件中创建新记号，切换到从这些记号（由 `\\toks`创建）所在的位置获取下一输入；也就是所谓的 *记号寄存器* ，它只是 TeX 所知道的一个内部内存位置（这里是寄存器 100）。

此外，记号列表还可以由若干 TeX 命令在内部即时生成。一个例子是命令 `\\jobname` ，它会生成一系列字符记号——文件名中每个字符对应一个记号。另一个例子是 `\string` 命令；例如

```latex
\string\mymacro
```

会为名称中的每个字母生成一系列字符记号 `\mymacro`——包括开头的 `\` 字符。我们会在本文末尾更仔细地看看一些“生成记号的命令”。

## 记号列表：用类比来解释

除非你有编程背景和/或一些计算机科学知识，否则“记号列表”这个概念可能会有点模糊，也许还有点令人困惑。不过，如果你希望熟练编写 TeX/LaTeX 宏，那么对 TeX 记号、记号列表和类别码（`\catcode`）的良好理解将会非常有用。

在这一节中，我们将用一个类比来解释/说明 TeX 记号列表的核心思想/原理：TeX 如何在内存中存储记号。值得花些时间读完，因为记号列表是 TeX 的一个 *基础* 方面，值得稍微更详细地理解一下。

### 记号列表：一个类比（思想实验）

我们将进行一个“思想实验”，以便为理解 TeX 记号列表提供基础。假设你可以使用一大批容器，比如几百个 Tin——我们不能用“盒子”这个词来描述思想实验中的容器，因为当然，“盒子”在 TeX 中有非常特定的含义，与我们这里的讨论无关。所以我们把这些容器称为“Tin”，其中每个 Tin：

* 外部印有唯一的识别编号；
* （内部）分成两个隔间。

这两个隔间的设计如下：

* 左侧隔间装放你想放进 Tin 的物品；
* 右侧隔间用来放一张纸，你可以在上面写一个数字：表示另一只 Tin 的编号。

![测试](/files/b930570fb33052b02a4a862e5467df96b90b8a29)

假设你有一组比如说 5 个物品，并且你想把这组物品存储到这些 Tin 中；但遗憾的是，每个 Tin 只能容纳 1 个你想存储的那种物品。

为简单起见，假设我们想存储 5 个有色圆圈：

![{{{alt}}}](/files/4d566868e04aef1ee49dddf5b6e1d76048f1d2df)

此外，当你回头从你的存储系统（Tins）中取回这些物品时，这些物品 *必须* 必须按照特定顺序被取回/找到——也就是它们被存放的顺序：这个顺序必须保持。你该如何做到这一点？

我们可以利用这样一个事实：每个 Tin：

* 外部都带有一个唯一的识别编号；
* 有 2 个隔间——其中只有 1 个我们会用来装物品，另一个则放着一张写有另一个 Tin 编号的纸。

我们假设每个 Tin 都是空的——不过你完全可以打开任意一个 Tin 来检查它是否为空；如果不是，就继续找下一个，直到找到一个空 Tin。

我们可以这样做。把第一个物品（深绿色圆圈）放进我们的某个 Tin 里（例如 Tin 124），并记下这个第一个 Tin 的编号——这个第一个 Tin 的编号是什么并不重要，重要的是我们把它记下来并保存起来以便日后使用。

![{{{alt}}}](/files/0fd6c882e146abfb4b681174ffd6d0bd3afea95a)

找到第二个 Tin——任意一个 Tin 编号都可以（例如 Tin 432）——并记下它的编号。把这个第二个 Tin（432）的编号写在一张纸上，并把这张纸放进 *第一个 Tin* （Tin 124）中。然后把第二个物品（浅绿色圆圈）放进第二个 Tin。于是，我们目前有以下情况：

* 一张不放在 Tin 里的纸条——上面写着第一个 Tin 的编号是 124（它装着我们的第一个物品）；
* 在 Tin 124 里面，我们又加了一张纸条，说明下一个物品在 Tin 432 中。

本质上，我们已经 *链接* 了前两个 Tin：我们知道从哪里开始（Tin 124），并且 Tin 124 中的一张纸条告诉我们下一个物品在哪个 Tin（Tin 432）。

![{{{alt}}}](/files/746d54624e440e8037f9786bf44db4028ada13bc)

然后我们找到第三个 Tin，把它的编号（例如 Tin 543）写在一张纸上，并把它放进 *第二个* Tin（编号 432）中。然后我们把第三个物品（红色圆圈）放进第三个 Tin。

现在我们已经按顺序把三个 Tin 连起来了：我们的起点 Tin 124（深绿色圆圈）→ Tin 432（浅绿色圆圈）→ Tin 543（红色圆圈）→…

![{{{alt}}}](/files/114a9a8d0664ea6a4c378ae0bbb308340db37262)

用 Tin 213（浅蓝色圆圈）和 Tin 102（深蓝色圆圈）对最后两个物品（浅蓝色和深蓝色圆圈）重复这个过程。

![{{{alt}}}](/files/062ff0d99ee8be267a6df59c6f6cb1ff37d9471c)

现在我们已经把全部 5 个 Tin 连接在一起了（使用每个 Tin 的数字标识符），并且能够只需依次访问每个 Tin，就把所有存储的物品按正确顺序取回——拿出物品并查看那张告诉我们下一个物品在哪个 Tin 的纸条。

### 那么列表中的最后一项（Tin 102）呢？

为什么我们尤其要关注这一项？到目前为止，我们已经把每个物品都存放在一个 Tin 中，并附上一张说明下一个物品在哪个 Tin 的纸条：那么对于列表中的最后一个物品，这张纸条应该写什么——因为没有下一个 Tin 了。

当我们到达最后一项（Tin）时，必须一眼就能看出这个 Tin（装着最后一个物品）就是我们列表中的最后一项——我们不需要再去找另一个 Tin，因为根本没有。做到这一点的一种方法，是在我们的最后一个 Tin（102）中放入一个“特殊”的 Tin 编号。只要我们选择一个不是真实 Tin 编号的唯一编号，任何数字都可以——例如“Tin -1”、“Tin 0”：只要我们知道“Tin -1”或“Tin 0”等会立即告诉我们停止查找就行了，因为这就是最后一个了，因此不会再有更多物品需要取回。

### 从“物品”和“Tins”到记号和 TeX

现在我们需要从这个类比转到更接近 TeX 现实的描述。首先，与其把不同颜色的圆圈存放在我们想象中的 Tin 里，不如把这些 Tin 想成存储 TeX 记号：简单的整数。这是把类比迁移到软件（TeX）领域较容易的部分。但带有“隔间”的实体编号 Tin 对应的软件版本会是什么呢？

我们不想过多地深入编程概念，但你可以把我们的“Tin”想成是几字节的计算机内存，被“打包”成一个方便的存储单元。类比中使用的每个 Tin 的数字标识符，可以看作计算机内存中每个小内存包所在的位置。在 TeX 内部，这些小存储包被称为“内存字”——这个术语反映了 TeX 创建时的年代（20 世纪 70 年代）。这些“内存字”是 TeX 内部使用的基本构建块，但我们这里不需要进一步展开——想了解更多细节的人可以参考一篇关于 [作者个人博客](http://www.readytext.co.uk/?p=3537).

用计算机编程术语来说，我们正在讨论的是一种 [*链表*](https://en.wikipedia.org/wiki/Linked_list)：TeX 记号列表是由 TeX 的存储容器构成的一个链表，这些容器称为 *内存字* 其中每个内存字都可用于存储：

* a *值*：记号的值（一个整数）；
* a *链接*：包含列表中下一个记号的下一个内存字的内存位置。

## TeX 在哪里使用记号列表？

无处不在！这是因为 TeX/LaTeX 宏定义（例如一个 LaTeX 命令）是以一种（稍微特殊化的）记号列表形式存储的——这里的特殊化是指，它包含了你在“标准”记号列表中看不到的记号（与匹配宏参数等有关）。不用担心这一点，因为我们会在未来的文章中讨论这些细节。

### 一个宏示例

一个宏可以看作由三部分组成：

```
\def\<macro name><parameter text>{<replacement text>}
```

请注意，你可以不用 `\def` 而是可以使用 `\edef`, `\gdef` 或 `\xdef`.

**给 LaTeX 用户的说明**：这里我们使用原始的、底层的 TeX 命令（称为 *原始命令*）。LaTeX 用户更熟悉通过 LaTeX 的 `\newcommand` （它本身也是一个宏）来创建宏。

当你要求 TeX 创建（定义）一个宏时，它会创建一个记号，表示 `<宏名>` 以及一个 *记号列表* ，它表示组合后的 `<参数文本>` 和 `<替换文本>`。TeX 会小心地存储所有内容，以便表示 `<宏名>` 的记号能链接到表示其定义的记号列表（`<参数文本>` 和 `<替换文本>`).

例如，如果我们定义 `\mymacro` 如下所示：

```latex
\def\mymacro abc #1 defz{I typed \"#1\"!}
```

我们可以看到它的组成部分是：

* `<宏名>` = `mymacro`
* `<参数文本>` = `abc #1 defz`
* `<替换文本>` = `我输入了“#1”！`

例如，你可以调用 `\mymacro` 如下所示：

```latex
\mymacro abc THIS TEXT defz
```

这会得到 `我输入了“THIS TEXT”！` 被排版出来—— `abc` 和 `defz` 已 *不* 排版出来。 `abc` 和 `defz` 是一串字符记号，用于 *分隔* 宏参数 `#1` 并在你的宏调用被 TeX 成功处理后被吸收并丢弃。

当你定义 `\mymacro`时，存储其中的记号模式会充当一个“模板”，TeX 可以据此判断：

* 你输入中的哪些记号是分隔符记号；
* 你输入中的哪些记号实际上构成了宏的参数（这里，你在 `#1` 的调用中所使用的内容）。 `\mymacro`).

你必须调用 `\mymacro` 替换 `<参数文本>` 时，所包含的分隔符必须与定义它时使用的完全相同——这也包括使用类别码完全相同的字符分隔符。如果用于 `<参数文本>` 调用的分隔符 `\mymacro` 与用于定义它的分隔符（也就是存储在内存中的“模板”）不同，那么 TeX 就会相当困惑——当它尝试处理 `\mymacro` 时，它将无法匹配保存在内存中的“模板”。

当 TeX 看到你在调用某个宏时，它会扫描你的输入文本来创建新的记号，并逐个记号地尝试将它们与记号列表 `<参数文本>` 作为宏定义一部分存储的模板进行匹配。如果你输入文本中使用的分隔符生成了一串与“模板”中存储的记号不匹配的记号，那么 TeX 通常会报错。

TeX 非常讲究——记住，字符记号是字符码和类别码的组合：如果你改变某个字符的类别码，你就会得到由该字符产生的不同记号值。

假设我们把 `z` 的类别码改成，比如说 12——通常它是 11——然后像这样调用我们的宏：

```latex
\catcode`z=12
\mymacro abc THIS TEXT defz more text here...
```

这一次它不会起作用，因为 `z` 的类别码已经改变。你会看到类似这样的错误：

```latex
参数失控？
THIS TEXT defz
！段落在 \mymacro 完成之前结束。
<重新读取>
\par
l.22
```

当 TeX 读取并扫描 `z` 在 `defz` 时，它无法将其识别为以下内容的结尾： `\mymacro`的 `<参数文本>` 在你的输入文件中使用的……。在看到这个错误的 `z` 之前，TeX 一直正确匹配前三个字符 `def` 但那个 `z` （类别码为 12）却绊住了 TeX 的扫描。 `z` 假设 *在我们* `\mymacro`定义时具有类别码 11：这会导致记号值 256×11 + 122 = 2938 被存储为 `\mymacro`的定义的一部分（即存储为“模板”的一部分）。不过，类别码为 12 时， `z` 现在会创建记号值 256×12 + 122 = 3194。由于（ `z`）从你的输入中读入的记号值（3194）与存储的 `z`-记号不匹配，存储的 `<参数文本>` 记号列表模板（2938）中的记号不匹配，TeX 会继续扫描你的输入。TeX 将继续扫描宏后面的文本（*这里还有更多文本* ……）以寻找额外记号——试图将存储的模板与你输入中找到的记号匹配起来。它大概找不到正确的记号模式，于是会产生错误，因为 TeX 会“越过”你的输入并错误地读取额外文本来创建额外记号——这些额外记号本不该在此时被读取，几乎肯定会生成错误。

我们会在未来的一篇文章中更详细地讨论这一点。

## 记号列表的其他用途

用于创建/存储记号列表的其他命令包括：

```latex
\toks<n>={...}
\everypar={...}
\everymath={...}
\everydisplay={...}
\everyhbox={...}
\everyvbox={...}
\output={...}
\everyjob={...}
\everycr={...}
\errhelp={...}
```

这些命令中的每一个都会从大括号‘{...}’内的字符和命令创建一个记号列表，而这组记号打算在某些情况下被重复使用。例如， `\everypar={...}` 会创建并存储一组记号（一个记号列表），TeX 会在开始新段落之前立即将其注入输入中。

## 记号列表的隐藏用途：示例

在最后一节中，我们将看看一些记号列表以你可能意想不到的方式被使用的实际示例。

### 示例 1：\uppercase{...} 和 \lowercase{....}——临时记号列表

除了显式创建记号列表的命令外，在某些情况下，TeX 还会生成一个隐藏的、临时的内部记号列表，以便进行某些特殊处理。请记住，当 TeX 读取/处理你的输入字符/命令时，它们会被转换成记号：这是 TeX 引擎所处理的基本构件。

一个很好的例子是命令 `\\uppercase{...}` 或 `\lowercase{...}` 因为它们的运作方式，乍一接触时可能会相当令人困惑。一旦你理解了它们在做什么——更深层地在 TeX 内部、对用户不可见——它们的行为就会更容易理解。

假设你有一串简单的字母，想把它们变成大写——例如 abcde 并将其转换为 ABCDE。用 TeX 的 `\\uppercase` 命令设置为特定值：

```latex
\uppercase{abcde}
```

会使 TeX 输出 `ABCDE`。现在假设我们想把这串简单字母保存起来供以后使用——也就是说，我们不想立即输出它们，因此我们将使用 TeX 唯一的 *内部* 用于保存数据的机制——而不是外部（文件）机制：使用记号列表。我们可以通过创建一个宏或使用显式的记号列表命令来做到这一点：

```latex
\toks100={abcde}
\def\mychars{abcde}
```

然后，在某个时刻，你可能会决定想要重新使用这串字母，但这一次要用大写；于是你尝试

```latex
\uppercase{\the\toks100}
```

和

```latex
\uppercase{\mychars}
```

但遗憾的是，这两种方法都不起作用。为什么？

### 秘密记号列表！

要理解这些命令 `\\uppercase{...}` `\lowercase{...}` 实际上如何工作，我需要窥探 TeX 的内部机制，因此下面的解释就来源于此。

当 TeX 检测到以下任一形式： `\uppercase{<material>}` 或 `\lowercase{<material>}` 时，TeX 首先会从 `<material>` 由随后跟在 `\\uppercase{...}` 或 `\lowercase{...}` 命令后面的‘{’和‘}’括起来的内容创建一个（临时的）内部记号列表——这个临时记号列表是 TeX 内部的。

一个关键点，也是理解 `\uppercase{<material>}` 和 `\lowercase{<material>}` 实际上如何工作的核心，是其中包含的任何命令或宏都不会被 `<material>` 不是 *展开*：TeX 所做的只是从放在 `{...}`。在 `\uppercase{<material>}` 或 `\lowercase{<material>}` 的过程中，大括号之间的任何内容都不会执行：它们只是被转换成记号。

在……之后 `<material>` 环境中输入 '#' 字符。即使是 `{...}` 在它被转换成一个（临时的）记号列表后，TeX 会再次逐个查看列表中的每个记号，并测试它是否是一个 *字符* 记号或者一个 *命令* 记号（使用记号的数值）。如果 TeX 检测到一个字符记号，它会修改该记号以调整字符的大小写（取决于 `\\uppercase` 或 `\lowercase` 正在处理）。TeX 会直接忽略任何命令记号，不会“深入查看”这些命令记号来看看它们表示或包含什么（例如包含字符的宏）——它们只是被跳过：只有字符记号才会真正被大小写转换操作处理/影响。

所以，例如，如果我们发出这样一个 TeX 命令： `\uppercase{abcde}` TeX 会从 `abcde` 创建一个只包含字符记号的记号列表：这些记号都会被调整，生成一串表示 A、B、C、D 和 E 的修改后记号。这些修改后的记号会被送回 TeX 的输入处理器，从而导致 `ABCDE` 被排版出来。然而，如果我们把字符 *存储在宏中*——例如 `\def\mychars{abcde}`——并尝试像这样把它们转换为大写：

```latex
\uppercase{\mychars}
```

那么它就会失败，而 abcde 会被排版出来——并不会像你预期的那样得到 ABCDE。如果我们再尝试把字符存储在像 `\toks0={abcde}` 这样的记号列表中，并执行 `\uppercase{\the\toks0}` 那么，再一次， `\\uppercase` 将会失败，因为记号列表将完全由不受 `\\uppercase`.

以我们的宏为例， `\mychars`，在 TeX 检测到 `\\uppercase` 在输入中时，TeX 会查找 `\\uppercase` 并执行它，从 `{\mychars}`。显然，该临时记号列表只包含一个记号，这个记号不是字符记号，而是表示我们的宏命令 `\mychars`：因此，就执行 `\\uppercase`而言，该记号会被忽略——`\mychars` 并不表示一个字符记号。然而，如上所述，一旦 `\\uppercase` 完成其工作，临时记号列表（由 `\\uppercase`的动作创建）就会被送回 TeX 完整的输入处理（扫描）机制中。当 TeX 重新读取该记号列表时，它会检测到一个表示我们 `\mychars` 宏的记号，TeX 会展开它并生成一串字符来排版 abcde——仍然是小写，因为它们被“包裹”在宏中，因此对 `\\uppercase`.

一旦 TeX 重新检查为 `\\uppercase{...}` 或 `\lowercase{...}`创建的临时记号列表，并处理了任何字符记号后，它就会切换为使用该临时记号列表作为输入源：进行字符排版（已处理的字符记号）以及执行命令和宏。

### 这该如何修复？

因为 `\\uppercase{...}` 或 `\lowercase{...}` 只会作用于字符记号，因此我们需要一种方法来“强制拆包”宏 `\mychars` （或包含在一个 `\\toks` 寄存器）中的字符，然后再让 `\\uppercase{...}` 或 `\lowercase{...}` 对其进行处理。所谓“拆包”，我们实际指的是 TeX 的过程： *展开*:

* 用 TeX/LaTeX 命令对应的 *序列* 记号 *来替换该命令* (*例如，一个宏*) *所组成，* 或
* 生成命令旨在 *产生*。一个会生成记号的命令例子是 `\\jobname`，它会生成一串字符记号，表示正在处理的主 TeX 文件的名称。

#### 更底层的魔法：scantoks(..., ...)

这里我们其实是在探查 TeX 内部工作机制的一些更隐秘的角落，所以除非你喜欢细节，否则可以忽略这一节……

在 TeX 检测到 `\\uppercase` 或 `\lowercase` 在输入流中时，它会执行一个名为 `scantoks(..., ...)` 的内部函数，其职责是使用开头的‘{’和结尾的‘}’之间的内容生成记号列表——如前所述，该记号列表随后会被检查，以检测（然后调整）任何字符记号，从而按需要改变字符大小写。请注意，我们这里说的是 `scantoks(..., ...)` ，指的是 TeX 引擎源代码中内置的内部函数——这里并不是把它当作控制序列的名称来指代。

作为其工作的一部分， `scantoks(..., ...)` 可以被指示是否展开它正在构造的记号列表，而对于 `\\uppercase` 和（`\lowercase`）来说，它不会展开这些记号：它只是创建它们并把它们放进一个记号列表中。

首先， `scantoks(..., ...)` 要做的是检查是否有一个开头的‘{’（或者任何类别码为 `\catcode` 1）因为它必须确保用户没有犯语法错误而忘记写开头的‘{’（或者任何类别码为 1 的字符）——因为需要一个类别码为 1 的字符来界定要被记号化的项目列表的开始。

诀窍就在这里：寻找开头‘{’的任务会触发 `scantoks(..., ...)` 运行 TeX 的展开过程，这意味着下面这些例子会奏效：

```latex
\let\ob={
\uppercase\ob abcde}
\def\obb{\ob}
\uppercase\obb xyz}
```

以 `\obb`为例，它是一个宏，会被识别为一个 *可展开命令* 并由 TeX（通过 `scantoks(..., ...)` 函数）在寻找开头大括号（任何类别码为 1 的字符）时适当地展开。也就是说，我们可以使用这个“`\\expandafter` 技巧”来达到我们的目标：把宏中的字符“拆包”出来——也就是展开它。请注意， `\\expandafter` 也属于一种 *可展开命令*，所以 TeX 会在这里对其进行处理，并让它作为寻找开头‘{’（或者任何类别码为 1 的字符）的一部分发挥作用。

所以，如果你定义：

```latex
\toks0={abcde}
\def\mychars{abcde}
```

并这样做：

```latex
\uppercase\expandafter{\mychars}
\uppercase\expandafter{\the\toks0}
```

在这两种情况下，你现在都会看到 ABCDE 被排版出来，因为 `\\expandafter` 会促使对 `\mychars` 和 `\the\toks0`进行“拆包”（展开）——两者都会导致 `\\uppercase` 看到一串字符记号，从而可以对其进行处理来改变大小写。

### 示例 2：\string——更多临时记号列表

在内部，TeX 将 `\string` 作为其所谓的“convert”命令之一：执行“convert to text”操作。该 `\string` 命令旨在将一个 token 转换为人类可读的文本版本——也就是说，把该 token 最初生成时所对应的、可读的字符字符串排版出来。

例如 `\string\hello` 会创建一个临时 token 列表，其中包含字符 \\, h, e, l, l, o——是的，甚至包括开头的‘\’。一旦该 token 列表被创建，TeX 就会重新读取它，然后命令“`\hello`”的文本就会被排版出来——是的，如果你选择了正确的字体，连‘\’也会被排版出来……

你可能会想，TeX 通常是用转义字符来触发扫描器创建一个命令 token 的，那么为什么它在这里还能排版出转义字符呢：为什么不会那样做？答案与类别码有关：通常，一个‘\’字符的 catcode 是 0（转义字符），但当 `\string` 生成其内部 token 列表时，它会做一些稍有不同的事情。它创建字符 token 列表时，会给除空格字符之外的所有字符分配类别码 12，而空格字符则分配 catcode 10——请记住，字符 token 是通过 256 x catcode + ASCII 值计算出来的。因此，当 TeX 重新读取（输入）那个临时 token 列表时， `\string` 由 `\hello`生成时，TeX *看不到转义字符* 因为‘\’对应的 token 是用 catcode 12 而不是 0 计算出来的：TeX 只是把‘\’当作普通字符并将其排版出来。

严格来说，我们也许应该指出，TeX 在检测到输入中的转义字符时，并不会真正为它们生成一个 token。一旦它识别出一个类别码为 0 的字符，该字符就只是用来“触发”生成一个控制序列 token：一旦它触发 TeX 完成这件事，转义字符就已经完成使命，不再被考虑。

### 技术说明

一个名为 `\showtokens{...}` （由 e-TeX 引擎引入）可以显示 token 列表（在日志文件中）。摘自 e-TeX 手册：

> 命令 `\showtokens{<token list>}` 显示 token 列表，并允许显示那些不能通过 `\show` 或 `\showthe`显示的量，例如：
>
> ```latex
> \showtokens\expandafter{\jobname}
> ```

## 总之

在 TeX 源代码的第 291 节（见 [TeX：程序](https://www.amazon.co.uk/Computers-Typesetting-TeX-Program-TEX/dp/0201134373)第 122 页）中，Knuth 对 token 列表的描述如下：

> “token 列表是 mem 中由单字节点组成的单链表，其中每个字都包含一个 token 和一个链接。宏定义、输出例程定义、标记、 `\write` 文本，以及其他一些东西，TeX 都以 token 列表的形式记忆，通常前面还会有一个节点，其“token\_ref\_count”字段中存放引用计数。”

初读这段话可能不太容易理解，但希望现在它多少会更有些意义了。


---

# 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/shen-ru-wen-zhang/54-what-is-a-tex-token-list.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.
