> 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/05-an-introduction-to-endlinechar-how-tex-reads-lines-from-text-files.md).

# \endlinechar 简介：TeX 如何从文本文件读取行

作为人类，我们更喜欢按行查看和编辑文本文件。一旦我们觉得某一行文本已经够长，就会在（文本编辑器中）按“回车”来表示该行结束。在幕后，你的文本编辑器会把这理解为一条指令，即在 [换行符](https://en.wikipedia.org/wiki/Newline) ，即你决定断行的位置。

不过，事情并没有这么简单：问题在于，不同的操作系统对什么构成换行符有不同的定义。更糟的是，Windows 会根据文件是否以所谓的 [*二进制模式* 或 *文本模式*](https://docs.microsoft.com/en-us/cpp/c-runtime-library/text-and-binary-mode-file-i-o?view=vs-2017)打开而以不同方式处理换行符。结果是，取决于宿主操作系统，文本文件中的各行可能会以不同的字符组合结束，这些字符称为 *回车符* （ASCII/Unicode 字符 13）和 *换行符* （ASCII/Unicode 字符 10）：分别记作 `\r` 和 `\n` 。

显然，为了保持系统无关性，TeX 需要一种方法来处理它需要读取和处理的文本文件中，用于结束一行的不同字符所带来的各种差异。

### TeX 的输入缓冲区

你可能会感到惊讶，也可能不会：TeX 引擎（包括 LuaTeX 和 XeTeX）是按行读取输入文件的：它们不会把整个文本文件一次性读入内存。尽管 TeX 引擎处理的大多数文本文件，与现代设备上的可用内存相比都微不足道，但文件中的每一行都会被单独读取并存储在一个小的内部缓冲区中。当然，TeX 读取和存储一行的过程还有一些额外的细节。

## “按我的方式来”——TeX 的 \endlinechar 命令

当 TeX 从输入文件中再读取一行文本时，它会执行两项“整理工作”：

* 它会移除行尾发现的任何终止换行字符（\r 或 \n）——也就是说，它会剥除文本文件最初保存到磁盘时添加的所有行尾标记；
* 它还会移除该行末尾发现的所有尾随空格字符。

这两个过程发生在 TeX 真正开始扫描该行中的字符之前：可以把它们看作是为下一阶段处理（扫描）做准备的一种“整理工作”。因此，在读行过程的这一初始阶段，TeX 已经剥离了所有平台相关的行尾标记（以及任何尾随空白）：那么 TeX 将如何知道（检测）该行在哪里结束呢？TeX 还有一个“绝招”： `\endlinechar` 命令。

为了避免平台相关的换行字符问题，TeX 引入了 `\endlinechar`，一个用户可定义的参数，TeX 用它在刚从文件中读取的一行文本末尾插入自己的行尾字符。再次注意，这发生在 TeX 真正开始扫描字符之前——这是 TeX 在准备开始读取（扫描）该行中实际字符之前的最后一步“整理工作”。

TeX 会使用存储在 `\endlinechar` 中的值来添加它自己的行尾终止符，前提是 `\endlinechar` 被正确定义——在 Knuth 的 TeX 中，这意味着它的值必须大于 -1 且小于 256。通常， `\endlinechar` 会被赋值为 13：也就是回车符——在编程文献中通常记作 `\r` 。

但如果你在输入中的某处写下 `\endlinechar=-1` ，那么下一次 *TeX* 从文件中读取一行文本时，它将 *不* 不会 `\endlinechar` 在行尾添加任何额外的终止符。因此，在你将`\r`):

```latex
\endlinechar=13
```

TeX 的 16 个类别码之一（值为 5）被保留用于识别“行尾”字符，而这通常就是 `\endlinechar` 所插入的字符——只有在 `\endlinechar` 的值被设为适当值时才会插入。

## 行尾处理摘要

虽然这些细节相当底层，但对于任何想要探索编写处理读取文本行的宏的人来说，它们都会很有意思。

1. 当 TeX 从你的文件中读取一行时，它会首先剥除文件保存时由你的文本编辑器添加的所有行尾字符（`\r` 和 `\n`）。此外：

* TeX 还会剥除该行末尾的任何尾随空格字符；
* TeX *不会* 移除尾随的制表符字符（ASCII 字符代码 9）。

**补充说明**：LuaTeX 的源代码文件之一，其中包含执行这种空格剥除的代码，文件中有如下注释：

> （引自文件 `luatex.c`）“David Fuchs 提到，这种\[空格]剥除是为了确保 TeX 文档的可移植性，因为当时某些系统上的固定记录“行”会用空格填充，例如 IBM VM/CMS 和 OS/360。”

3. 在步骤（1）之后，TeX 会添加（插入）一个额外字符，其值存储在 \endlinechar 中（前提是它被适当地定义：大于 -1 且小于 256）
4. \endlinechar 通常被设为 13（`\r`），这意味着在步骤（2）中添加的字符通常是字符 13（`\r`）——不过，当然，你可以将 \endlinechar 设为其他值，通过宏编程实现特殊效果。
5. 当其输入扫描例程检测到字符 `\r` （字符代码 13）位于其内部缓冲区末尾时，TeX 会像往常一样检查其类别码，以决定如何处理它。
6. 字符 13 *通常* 的类别码值为 5（“行尾”），当然，除非它的类别码已被更改——有些宏会将行尾字符设为活动字符，以便执行复杂的处理。
7. 根据 TeX 的内部状态（实际上就是它正在做什么），TeX 可以把该行尾字符（通常是 `\r`，类别码 5）变成一个空格字符——这就是行尾字符如何变成空格的。
8. 还要注意，TeX 使用类别码为 5 的字符来检测它何时读取到空行，并需要生成一个 `\par` 记号。

下面的图示对步骤（1）和（2）进行了直观总结：剥除换行字符和尾随空格字符，并插入 `\endlinechar` ，以便为扫描输入做好准备。

![TeX 如何使用 \endlinechar](/files/8a004c446b4a9a16c63ac981dea60022a1b38a7f)


---

# 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/05-an-introduction-to-endlinechar-how-tex-reads-lines-from-text-files.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.
