为什么 LaTeX 告诉我我的外部文本文件的最后一行包含标记 \par?

为什么 LaTeX 告诉我我的外部文本文件的最后一行包含标记 \par?

为什么 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日附录:

David Carlisle 在他的回答中指出:

tex.web 9507 说

@ 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

相关内容