为什么 LaTeX(TeX Live 2020、Debian)告诉我 SomeLines.txt 有第 6 行包含标记\par
?
我知道如果\endlinechar
为 13,并且字符 13(回车符)的 catcode 为 5,则会得到\par
一个空行,因为回车符会附加到空行,并且当回车符属于类别 5(行尾)且 TeX 的读取设备处于状态 N 时,会处理该回车符。
因此,我认为 TeX 读取一行输入的例程“认为”第 5 行之后有一个空行/空记录,并附加了行尾字符,其类别为 5(行尾)。处理该字符时,TeX 的读取装置处于状态 N,导致附加的行尾字符产生控制字标记\par
。
如果是这样:
- 为什么 TeX 认为第 5 行之后有一个空行/空记录?
- 所有 TeX 发行版的读取一行输入的例程都是这么认为的吗?
\begin{filecontents*}{SomeLines.txt}
Line1
Line2
Line3
Line4
Line5
\end{filecontents*}
\newread\SomeLinesRead
\newcount\SomeLinesCount
\def\CountLinesLoop{%
\ifeof\SomeLinesRead
\else
\immediate\read\SomeLinesRead to \Thisline
\global\advance\SomeLinesCount by 1 %
\message{^^JLine \number\SomeLinesCount="\Thisline"}%
\expandafter\CountLinesLoop\fi
}
\immediate\openin\SomeLinesRead SomeLines.txt
\SomeLinesCount=0
\begingroup%
% Changes of the value of \endlinechar are reflected in the line "Line6=...".
% If \endlinechar denotes the number of the code-point of a
% character of category 5(end of line), the state of the reading-
% apparatus is reflected, too, which apparently is N when the
% character appended to line/record 6 is processed.
%\endlinechar=`\A % -> Line 6="A"
%\endlinechar=`\B % -> Line 6="B"
\CountLinesLoop%
\endgroup%
\immediate\closein\SomeLinesRead
\message{^^JTotal amount of lines: \number\SomeLinesCount}%
\stop
控制台输出:
This is pdfTeX, Version 3.14159265-2.6-1.40.21 (TeX Live 2020) (preloaded format=pdflatex)
restricted \write18 enabled.
entering extended mode
(./test.tex
LaTeX2e <2020-10-01> patch level 4
L3 programming layer <2021-02-18>
LaTeX Warning: Writing file `./SomeLines.txt'.
Line 1="Line1 "
Line 2="Line2 "
Line 3="Line3 "
Line 4="Line4 "
Line 5="Line5 "
Line 6="\par "
Total amount of lines: 6 )
No pages of output.
Transcript written on test.log.
我假设:
终止循环的条件不是“如果到达文件的最后一个记录终止符”。
终止循环的条件是\ifeof
,即“如果到达文件末尾”。
有五行/记录(其中没有一行包含第 1 类(开始组)的不匹配字符),因此在这种情况下,单个\read
操作处理单个行/记录),因此有五个“记录终止符”和一个“文件结束符”属于一些行.txt,总共进行六次读取迭代,直到到达文件末尾。在第 6 次迭代中,“行/记录”被视为空,因为最后一个“记录终止符”和“文件末尾”之间没有任何内容。TeX 对
空行的处理以及 token 的产生\par
如上所述。
这个解释正确吗?
2021年11月3日附录:
@ An empty line is appended at the end of a |read_file|. @^empty line at end of file@>
似乎这意味着附加了一个空行/记录。
似乎这是故意的。
因此后续问题是:
为什么?有什么目的?
我是否忽略了 TeXbook 中有关此“功能”的文档?
这是您需要研究 tex.web 才能了解的众多功能之一吗?
2022年1月20日附录:
TeXbook 在第 20 章:定义(也称为宏)中说道:
要从打开的文件中获取输入,您可以说,
\read⟨number⟩to⟨control sequence⟩
并且控制序列被定义为无参数宏,其替换文本是从指定文件读取的下一行的内容。使用第 8 章的过程,根据当前类别代码,将此行转换为标记列表。
如果需要,将读取其他行,直到找到相等数量的左括号和右括号。一个空行被隐式地添加到正在被复制的文件的末尾。\read
。
2023年12月2日附录:
提问者(Ulrich Diez)推测隐式添加空行的原因如下:
使用有趣的文件系统/操作系统来检测文件是否为空需要尝试读取该文件并“查看”此尝试是否会立即到达文件末尾。
\read
因此,需要针对文件为空的情况确定尝试执行赋值的结果。
\read
隐式附加一个空行决定了在文件为空的情况下尝试执行分配的结果。
因此,对于这种有趣的文件系统/操作系统,隐式地附加一个空行使得可以执行分配\read
以调整开关,\ifeof
即使在相关文件为空的情况下也是如此。
因此,根据经验法则,首先让 TeX 执行 -assignment \read
。
然后让 TeX 执行\ifeof
-test。
如果\ifeof
-test 为假,则-assignment 过程中定义的宏的替换文本\read
将由来自文件的材料组成。
如果\ifeof
-check 为真,则在 -assignment 过程中定义的宏的替换文本\read
由不是来自文件的材料组成,而是来自 TeX 对隐式附加的空行所做的处理。
隐式添加空行也可以检查文件是否存在:
如果文件确实存在,则\ifeof
在打开文件进行读取后不会立即成立,而只有在执行\read
读取隐式附加的空行的 -assignment 之后才成立。
如果文件不存在,则\ifeof
在打开文件进行读取后立即为真,无需执行\read
分配。
\begin{filecontents*}{SomeLines.txt}
Line1
Line2
Line3
Line4
Line5
\end{filecontents*}
\begin{filecontents*}{SomeLinesB.txt}
\end{filecontents*}
\newread\SomeLinesRead
\newcount\SomeLinesCount
\def\CountLinesLoop{%
% In the course of reading what TeX makes of the implicitly appended
% empty line TeX also adjusts the \ifeof-switch:
\immediate\read\SomeLinesRead to \Thisline
\ifeof\SomeLinesRead
\message{^^JLine implicitly appended by TeX="\Thisline"}%
\else
% Only advance \SomeLinesCount in case the line just read is not the
% implicitly appended empty after which \ifeof yields the true-branch.
\global\advance\SomeLinesCount by 1 %
\message{^^JLine \number\SomeLinesCount="\Thisline"}%
\expandafter\CountLinesLoop
\fi
}
\message{^^J}%
\message{^^J}%
\message{^^JProcesing file SomeLines.txt}%
\message{^^J============================}%
\message{^^J}%
\immediate\openin\SomeLinesRead SomeLines.txt
\message{^^JFile SomeLines.txt does \ifeof\SomeLinesRead not \fi exist}%
\message{^^J}%
\SomeLinesCount=0
\begingroup
%\endlinechar=`\A % -> Line implicitly appended by TeX="A"
%\endlinechar=`\B % -> Line implicitly appended by TeX="B"
\CountLinesLoop%
\endgroup%
\immediate\closein\SomeLinesRead
\message{%
^^J%
\ifnum0=\SomeLinesCount
The file is empty.
\else
Total amount of lines of the file is: \number\SomeLinesCount.
\fi
}%
\message{^^J}%
\message{^^J}%
\message{^^JProcesing file SomeLinesB.txt}%
\message{^^J=============================}%
\message{^^J}%
\immediate\openin\SomeLinesRead SomeLinesB.txt
\message{^^JFile SomeLinesB.txt does \ifeof\SomeLinesRead not \fi exist}%
\message{^^J}%
\SomeLinesCount=0
\begingroup
%\endlinechar=`\A % -> Line implicitly appended by TeX="A"
%\endlinechar=`\B % -> Line implicitly appended by TeX="B"
\CountLinesLoop%
\endgroup%
\immediate\closein\SomeLinesRead
\message{%
^^J%
\ifnum0=\SomeLinesCount
The file is empty.
\else
Total amount of lines of the file is: \number\SomeLinesCount.
\fi
}%
\message{^^J}%
\message{^^J}%
\message{^^JProcesing non-existent file SomeLinesC.txt}%
\message{^^J==========================================}%
\message{^^J}%
\immediate\openin\SomeLinesRead SomeLinesC.txt
\message{^^JFile SomeLinesC.txt does \ifeof\SomeLinesRead not \fi exist}%
\immediate\closein\SomeLinesRead
\stop
控制台输出:
This is pdfTeX, Version 3.14159265-2.6-1.40.21 (TeX Live 2020) (preloaded format=pdflatex)
restricted \write18 enabled.
entering extended mode
(./test.tex
LaTeX2e <2020-10-01> patch level 4
L3 programming layer <2021-02-18>
LaTeX Warning: Writing file `./SomeLines.txt'.
LaTeX Warning: Writing file `./SomeLinesB.txt'.
Procesing file SomeLines.txt
============================
File SomeLines.txt does exist
Line 1="Line1 "
Line 2="Line2 "
Line 3="Line3 "
Line 4="Line4 "
Line 5="Line5 "
Line implicitly appended by TeX="\par "
Total amount of lines of the file is: 5.
Procesing file SomeLinesB.txt
=============================
File SomeLinesB.txt does exist
Line implicitly appended by TeX="\par "
The file is empty.
Procesing non-existent file SomeLinesC.txt
==========================================
File SomeLinesC.txt does not exist )
No pages of output.
Transcript written on test.log.
答案1
读取文件时,无论文件末尾是否有行尾字符,tex 的行为基本相同。\endlinechar
如果设置为合法字符值,则最后一行将被视为一行,并添加到每行的末尾。
tex.web 9507 说
@ An empty line is appended at the end of a |read_file|.
@^empty line at end of file@>
因此,如果\endlinechar
是正常的 13,而字符 13 的 catcode 为 5,那么照例最终会被报告为\par
。如果您设置,则为真\endlinechar=-1
之前的最后一行将被报告为空。\ifeof