我试图创建一个fileb.tex
可以\input
通过的.tex 文件filea.tex
,当输入时,它会停止输入并触发重写。
在我第一次尝试时错误地省略了\endinput
。
在此,我偶然发现了一些我不希望在省略的情况下出现的行为\endinput
:
这是filea.tex
:
\newwrite\filebwrite
%
% Create the initial fileb.tex:
\immediate\openout\filebwrite fileb.tex %
\immediate\write\filebwrite{This is fileb.}%
\immediate\write\filebwrite{{\string\tt\string\string\string\macro} was not used when writing it.}%
\immediate\write\filebwrite{\string\recreatefileb}%
\immediate\write\filebwrite{Something in fileb.tex that you should not see in the pdf.}%
\immediate\closeout\filebwrite
%
%
\def\recreatefileb{%
\immediate\openout\filebwrite fileb.tex %
\immediate\write\filebwrite{This is the re-written fileb.}%
\immediate\write\filebwrite{At the time of writing it {\string\tt\string\string\string\macro} expanded to: \macro}%
\immediate\write\filebwrite{\string\recreatefileb}%
\immediate\write\filebwrite{Something in fileb.tex that you should not see in the pdf.}%
\immediate\closeout\filebwrite
}%
%
fileb.tex is now processed.
\def\macro{Rewrite 1}%
\input fileb.tex % Here you should have: ... was not used when writing it.
\def\macro{Rewrite 2}%
\input fileb.tex % Here you should have: ... expanded to: Rewrite 1
\def\macro{Rewrite 3}%
\input fileb.tex % Here you should have: ... expanded to: Rewrite 2
%
% When looking at fileb.tex after compiling filea.tex, you should see:
%
% This is the re-written fileb.
% At the time of writing it {\tt\string\macro} expanded to: Rewrite 3
% \recreatefileb
% Something in fileb.tex that you should not see in the pdf.
%
\bye
当我编译时filea.tex
,我得到以下输出:
在编译期间也会fileb.tex
创建并重新创建filea.tex
。fileb.tex
编译后存在filea.tex
并且看起来符合我的预期:
This is the re-written fileb.
At the time of writing it {\tt\string\macro} expanded to: Rewrite 3
\recreatefileb
Something in fileb.tex that you should not see in the pdf.
我根本没有收到任何错误消息。
filea.log
看起来像这样:
This is pdfTeX, Version 3.14159265-2.6-1.40.19 (TeX Live 2019/dev/Debian) (preloaded format=pdftex 2020.2.13) 9 AUG 2020 11:50
entering extended mode
\write18 enabled.
%&-line parsing enabled.
**filea.tex
(./filea.tex
\filebwrite=\write0
\openout0 = `fileb.tex'.
(./fileb.tex
\openout0 = `fileb.tex'.
) (./fileb.tex
\openout0 = `fileb.tex'.
) (./fileb.tex
\openout0 = `fileb.tex'.
) [1{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] )</usr/share/texlive/t
exmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb></usr/share/texlive/texmf-di
st/fonts/type1/public/amsfonts/cm/cmtt10.pfb>
Output written on filea.pdf (1 page, 26042 bytes).
PDF statistics:
16 PDF objects out of 1000 (max. 8388607)
10 compressed objects within 1 object stream
0 named destinations out of 1000 (max. 500000)
1 words of extra memory for PDF output out of 10000 (max. 10000000)
终端显示如下:
$ pdftex -shell-escape filea.tex
This is pdfTeX, Version 3.14159265-2.6-1.40.19 (TeX Live 2019/dev/Debian) (preloaded format=pdftex)
\write18 enabled.
entering extended mode
(./filea.tex (./fileb.tex) (./fileb.tex) (./fileb.tex) [1{/var/lib/texmf/fonts/
map/pdftex/updmap/pdftex.map}] )</usr/share/texlive/texmf-dist/fonts/type1/publ
ic/amsfonts/cm/cmr10.pfb></usr/share/texlive/texmf-dist/fonts/type1/public/amsf
onts/cm/cmtt10.pfb>
Output written on filea.pdf (1 page, 26042 bytes).
Transcript written on filea.log.
我很困惑,有以下疑问:
为什么 .pdf 输出文件中会有短语
at you should not see in the pdf.
(上图中用红色标记)?
人们经常说 TeX\input
是逐行处理文件的。(TeX 的“眼睛”查看输入的一行,并将该行的字符传递给“嘴巴”进行标记……)
但似乎出现该短语的输入行的后面部分被处理了(两次)。
令我惊讶的是,看起来经过 TeX“嘴巴”的东西不是完整\input
行的字符,而只是输入行后面部分的字符。在重写时
fileb.tex
,没有\endinput
发生任何事情,因此fileb.tex
仍然打开并用于读取。为什么这不会产生错误消息?为什么在将文件用作输入文件时似乎可以重写该文件?Something in fileb.tex that you should not see in the pdf.
为什么.pdf 文件中会有这么多短语?这个短语总是单独出现在一行中。我假设当触发重新创建fileb.tex
时,该行尚未处理/尚未进入 TeX 的口中。\recreatefileb
fileb.tex
顺便说一句:下面的内容似乎符合我的想法:
filea.tex
:
\newwrite\filebwrite
%
% Create the initial fileb.tex:
\immediate\openout\filebwrite fileb.tex %
\immediate\write\filebwrite{This is fileb.}%
\immediate\write\filebwrite{{\string\tt\string\string\string\macro} was not used when writing it.}%
\immediate\write\filebwrite{\string\expandafter\string\endinput}%
\immediate\write\filebwrite{\string\recreatefileb}%
\immediate\write\filebwrite{Something in fileb.tex that you should not see in the pdf.}%
\immediate\closeout\filebwrite
%
%
\def\recreatefileb{%
\immediate\openout\filebwrite fileb.tex %
\immediate\write\filebwrite{This is the re-written fileb.}%
\immediate\write\filebwrite{At the time of writing it {\string\tt\string\string\string\macro} expanded to: \macro}%
\immediate\write\filebwrite{\string\expandafter\string\endinput}%
\immediate\write\filebwrite{\string\recreatefileb}%
\immediate\write\filebwrite{Something in fileb.tex that you should not see in the pdf.}%
\immediate\closeout\filebwrite
}%
%
fileb.tex is now processed.
\def\macro{Rewrite 1}%
\input fileb.tex % Here you should have: ... was not used when writing it.
\def\macro{Rewrite 2}%
\input fileb.tex % Here you should have: ... expanded to: Rewrite 1
\def\macro{Rewrite 3}%
\input fileb.tex % Here you should have: ... expanded to: Rewrite 2
%
% When looking at fileb.tex after compiling filea.tex, you should see:
%
% This is the re-written fileb.
% At the time of writing it {\tt\string\macro} expanded to: Rewrite 3
% \expandafter\endinput
% \recreatefileb
% Something in fileb.tex that you should not see in the pdf.
%
\bye
输出结果符合预期:
但即使在这里,我现在仍然有点困惑:
\endinput
TeXbook 第 214 页中的定义是:
\endinput
。扩展为空。下次 TeX 到达行末时\input
,它将停止从包含该行的文件读取。
在fileb.tex
(其他东西下面)您有两条输入线:
\expandafter\endinput
\recreatefileb
以下哪一行被视为“下次处理\endinput
TeX 后到达行尾\input
”的行?
我假设:
由于\expandafter
标记\recreatefileb
(以及包含的行的末尾\endinput
)在处理之前被处理\endinput
。
因此,在处理完之后,TeX 下次\endinput
到达包含字符串 的行尾时,将到达 - 行的末尾。因此,不会处理后续行。(如果只是无操作,也不会处理它。)\input
\recreatefileb
\recreatefileb
Something in fileb.tex that you should not see in the pdf.
\recreateb
我的假设正确吗?
如果是这样,就会出现以下问题:
当\recreatefileb
触发重写时fileb.tex
,\endinput
尚未处理。因此fileb.tex
仍被视为\input
要读取的文件。为什么虽然文件被视为要读取的文件,但可以重写它\input
?
如果不是这样,就会出现以下问题:
我哪里错了?
答案1
我将假设一个大致类似于 POSIX 的操作系统(也就是说,Windows 很奇怪并且可能表现不同)。
让我们从你的问题 2 开始:在别人写入文件时读取文件通常有很好的用例,例如,这允许实时监视日志文件(任何有效的实现tail -f
会这样做)、原始进程间通信等。另一方面,没有充分的理由说明它应该失败。如果应用程序无法处理它,它总是可以使用一些锁定系统。
对于其他要点,您必须知道“从文件读取一行输入”是标准库在系统调用之上实现的抽象,而系统调用没有“行”的概念。该级别的文件只是一堆字节。
因此,“读取一行输入”的请求是通过保留文件接下来 500 个字节的内部缓存来实现的,我总是扫描该缓存以查找行的下一个结尾。如果找到,则返回该行,否则将从文件中读取下一个字节。
因此,当重新创建文件时,原始文件的最后一行通常仍在缓存中,并直接返回,根本不查看实际文件。只有到达文件末尾时,库才会询问操作系统是否还有其他内容。请记住:在此级别,没有行,只有字节。因此,如果文件以前是 420 字节长,库会询问:“您能从位置 421 开始读取一些字节吗?”如果文件已更改,则此偏移量现在可能位于某行的中间。因此,系统成功返回文件的其余部分,库将其作为下一行返回(毕竟,它已在偏移量 420 处读取了一个换行符,因此 421“显然”开始了一个新行)。
整个问题表明这些系统可以处理更改的文件,但它们假设您只附加额外的内容,而不会更改现有的内容。
顺便说一句,这特定于更改现有文件。例如,文本编辑器更改文件的一种常见方法是删除旧文件并使用旧名称创建新文件(这有点复杂,但这是一般的想法)。在这种情况下,您仍然读取的文件仍然是已删除的旧文件,因此您不会从新文件中获取随机内容。(不过,您可以更可靠地读取旧文件的其余部分)