为什么需要 \everyeof{\noexpand} 来避免使用可扩展文件读取时出现“扫描定义时文件结束”的情况?

为什么需要 \everyeof{\noexpand} 来避免使用可扩展文件读取时出现“扫描定义时文件结束”的情况?

考虑以下代码:

\documentclass{article}
\usepackage{filecontents}
\begin{filecontents}{foo.txt}
abc
\end{filecontents}
\begin{filecontents}{bar.txt}
abc
\end{filecontents}
\begin{document}
\makeatletter
\def\einput#1{\@@input #1 \space}
\newcommand{\showexpanded}[1]{%
  \everyeof{\noexpand}%
  \long\edef\@tempcontents{#1}%
  \show\@tempcontents
}
\makeatother
\showexpanded{\einput{foo.txt}\einput{bar.txt}}
.
\end{document}

它的工作方式大致与预期一致,

$ pdflatex foo.tex
This is pdfTeX, Version 3.14159265-2.6-1.40.16 (TeX Live 2015/Debian) (preloaded format=pdflatex)
 restricted \write18 enabled.
entering extended mode
(./foo.tex
LaTeX2e <2016/02/01>
Babel <3.9q> and hyphenation patterns for 7 language(s) loaded.
(/usr/share/texlive/texmf-dist/tex/latex/base/article.cls
Document Class: article 2014/09/29 v1.4h Standard LaTeX document class
(/usr/share/texlive/texmf-dist/tex/latex/base/size10.clo))
(/usr/share/texlive/texmf-dist/tex/latex/filecontents/filecontents.sty)

LaTeX Warning: Writing file `./foo.txt'.



LaTeX Warning: Writing file `./bar.txt'.


(./foo.aux) (./foo.txt) (./bar.txt)
> \@tempcontents=\long macro:
->abc \space abc \space .
\showexpanded ...ontents {#1}\show \@tempcontents

l.18 ...expanded{\einput{foo.txt}\einput{bar.txt}}

?

但是,如果我将其更改\noexpand\relax,则会出现! File ended while scanning definition of \@tempcontents. 为什么会这样?有没有办法吃掉 EOF 而不会导致后续内容\space遗留?(我\noexpand通过货物崇拜获得了使用http://ctan.mirrors.hoobly.com/macros/latex/contrib/oberdiek/catchfile.pdf

答案1

深入研究 TeX 程序代码,我的主要发现是标记流中没有插入显式或隐式的文件结束标记。

当 TeX 看到\input命令时,它会执行一些文件读取维护代码 ( begin_file_reading) 并切换到新文件以进一步读取输入行。如果它看到命令\endinput或已到达输入文件的实际末尾,它会将标志设置force_eof为 true。后者用于确定end_file_reading在读取下一行时是否应从读取堆栈 ( ) 中弹出当前文件。然后处理返回到\input调用的位置。

如果force_eof在 TeX 尝试读取输入行时将 设置为 true,它将调用check_outer_validity,后者依次检查当前源位置是否允许文件结尾。它根据名为 的变量进行检查scanner_status。当扫描仪状态未指示正在形成定义、正在读取参数等时,检查将通过,TeX 将继续处理下一个标记。否则,将根据当前状态发出“失控”错误消息。此外,ε-TeX 将钩子中定义的材料添加\everyeof到标记流中。

从概念上讲,我们可以想象输入的 token 流

\everyeof{\noexpand}
\edef\@tempcontents{\@@input foo.txt \space}

当时\@tempcontents被定义为以下序列:

<contents of input file> <endline char> \noexpand <EOF> \space

<endline char>是 TeX 根据\endlinechar读取的每一行的值插入的字符。`\^^M默认情况下,它会扩展为空格字符,并导致控制台输出中的额外空格abc。接下来是标记列表的内容\everyeof,在本例中是这样的\noexpand

现在神秘的<EOF>标记出现了。这里最重要的是,<EOF>不是一个实际的或内部的标记,它只是 TeX 检查\outer在处理过程中此位置是否允许使用宏的短暂时刻。

\edef由于 TeX当时处于 状态,我们可能用来\everyeof诱使 TeX 忽略外部测试的命令必须是 a) 可扩展的,并且 b) 允许\outer宏跟随它们。根据代码中的提示,至少以下三个命令在此处有效:

  • \noexpand
  • \string
  • \meaning

它们之所以能起作用,是因为它们暂时切换到扫描仪状态普通的在读取下一个标记之前,从而否定了 中的错误条件check_outer_validity。TeX 一时茫然,认为自己处于正常处理模式,而实际上仍在扫描宏定义的结尾。


如果您还想删除令牌\space,可以使用以下命令:

\newcommand{\showexpanded}[1]{%
  \everyeof{\expandafter\@gobble\noexpand}%
  \long\edef\@tempcontents{#1}%
  \show\@tempcontents
}

输出

> \@tempcontents=\long macro:
->abc abc .
\showexpanded ...ontents {#1}\show \@tempcontents

相关内容