考虑以下代码:
\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