我想编写一些简单的宏来处理诸如交叉引用之类的事情,以便在纯 TeX 中使用它们。(我知道已经有专门用于此目的的宏,例如 Eplain 中的宏,但我想自己尝试一些不同的东西。)所以我需要知道如何从文件中读取以及如何写入文件。执行此类操作的 TeX 原语是什么?它们如何工作?
另一个问题:TeX 运行时可以“调用”其他程序吗?我的意思是:TeX 中是否有与 C 语言中的系统函数等效的函数?
答案1
TeX 具有用于读取和写入文件的\read
和\write
原语,当然还有\input
用于“在此处”输入整个文件的原语。例如,如果您查看 LaTeX 交叉引用机制,就会发现它使用\write
但避免逐行使用\read
,而是使用\input
适当设计的辅助文件。
很\input
容易理解,让我们关注\read
和\write
。它们都适用于文件流,文件流被赋予一个数字,但通常使用来分配\new...
。例如
\newread\myread
\openin\myread=myinput %
\newwrite\mywrite
\immediate\openout\mywrite=myoutput %
将设置一个名为 的读取\myread
和一个名为 的写入\mywrite
。请注意,我使用了\immediate
:\write
由于 TeX 页面构建器的异步特性,您需要确保\write
操作发生在“正确”的位置。(有关详细信息,请参见下文。)
例如,打开两个流后,我们可以写入输出。如果我们进行两次写入,一次是“现在”,一次是“延迟”
\def\foo{a}
\immediate\write\mywrite{\foo}
\write\mywrite{\foo}
\def\foo{b}
Hello
\bye
结果myoutput.tex
是
a
b
这是因为\write\mywrite
产生的 whatsit 仅在页面输出时执行。例如,如果您需要写入的内容包含页码,这将非常有用,因为只有在输出阶段才会知道页码。还请注意 的\write
作用类似于:除非您使用或\edef
阻止它,否则所有内容都会扩展。但请注意,此扩展是在实际执行操作时执行的,因此在使用延迟 时必须确保宏具有正确的定义。\noexpand
toks
\write
\write
原\read
语一次读取一行(除非括号不匹配)并以常规 TeX 方式进行标记。您可以使用 test \ifeof
on一次一行地循环遍历文件,但正如我所说,简单地读取包含交叉引用的文件\myread
通常更容易。\input
如果你想进行系统调用,那么“纯” TeX 真的帮不上什么忙。然而,Web2c 长期以来一直有一个特殊的“流”来允许逃逸到系统中:\write18
。这是一种安全风险,因此作为标准,在这样的逃逸中只允许一组受限的命令。例如,你可以这样做
pdftex --shell-escape myfile
允许所有逃脱:如果您自己编写了所有代码,则风险只会是弄乱!执行\write18
不会将任何内容反馈给 TeX:您需要安排以某种方式读取结果,可能\read
在辅助文件上使用。
如评论中所述,可用的附加语法扩展是\input|"<command>"
。这再次受到限制\write18
,但确实提供了一种可扩展的方法来获取来自 shell 命令的输入。
答案2
通过外部文件处理交叉引用时的另一个特殊问题是读取、打开、写入和关闭该文件的顺序。基本方案是:
\newwrite\fileout % allocations of the file number
\def... ... all macros which can be used in the file must be defined
\input file ... input only when file exists (the file doesn't exist at the first run)
... this input stores the data from file into internal macros
\immediate\openout\fileout=file ... this removes the data from file
... the file has zero length now
... document ... macros used in the document write (typically not immediately)
data to the file
\end ... this closes the file automatically,
but if you need to use the data from the file at the end of the document
(for example the TOC at the end of the document):
\vfil\eject
\immediate\closeout\fileout
\input file ... this creates the TOC at the end of the file.
\end
上面的方案表明您需要创建一个宏,\input
仅当该文件存在时才读取该文件。您可以使用以下\softinput
宏来实现此类目的:
\newread\testin
\def\softinput #1 {\let\next=\relax \openin\testin=#1
\ifeof\testin \message{Warning: the file #1 does not exist}%
\else \closein\testin \def\next{\input #1 }\fi
\next}