我正在尝试重新定义一个\tableofcontents
类似命令。
我是这样进行的:将显示的元素写入文件,当我想要显示时输入该文件。
如果我知道 toc 将在文档的开头(或结尾)被调用,那就没问题:我所要做的就是将\openout
(或\closeout
)命令添加到toc
命令中。但是,我不希望在这种假设下工作(主要是因为我不是唯一使用该代码的人,而且我想避免不明显的副作用)。
因此,我希望\openout
和命令在和\closeout
时自动发出。问题是,在这种情况下,我无法将辅助文件放在文档中间。我一直试图将文件的内容存储在宏中,但我遇到了各种问题,我猜这些问题与扩展有关。\begin{document}
\end{document}
\input
\begin{document}
浏览了一些相关问题后,我设法编写了以下代码:
\documentclass{article}
\newwrite\sommaire
\AtBeginDocument{%
\immediate\openout\sommaire=\jobname.som%
}
\AtEndDocument{%
\immediate\closeout\sommaire}
\newcommand{\sommaireline}[1]{%
\immediate\write\sommaire{#1}
}
\makeatletter
\IfFileExists{\jobname.som}%
{\edef\mysommaire{\@@input \jobname.som }}
{\edef\mysommaire{\message{No file \jobname.som}}}
\makeatother
\begin{document}
\sommaireline{aaaaa}
\mysommaire
\sommaireline{bbbbb}
\end{document}
但它仍然不起作用,现在我不明白为什么。
更准确地说,第一次编译顺利,文件也.som
按预期写入,但第二次编译失败,如下所示:
ERROR: File ended while scanning definition of \mysommaire.
...
ERROR: Too many }'s
都在第一线上\edef
。
一些相关问题:
答案1
只需使用 Heiko Oberdiek 的catchfile
包:
\documentclass{article}
\usepackage{catchfile}
\newwrite\sommaire
\AtBeginDocument{\immediate\openout\sommaire=\jobname.som }
\AtEndDocument{\immediate\closeout\sommaire}
\newcommand{\sommaireline}[1]{%
\immediate\write\sommaire{#1}
}
\IfFileExists{\jobname.som}%
{\CatchFileDef\mysommaire{\jobname.som}{}}
{\def\mysommaire{\message{No file \jobname.som}}}
\begin{document}
X\sommaireline{aaaaa}
\mysommaire
\sommaireline{bbbbb}X
\end{document}
\CatchFileDef
例如,您可以使用最后一个参数来进行分配
\CatchFileDef\mysommaire{\jobname.som}{\endlinechar=-1 }
将抑制输入文件中因行尾字符而产生的最后一个空格。
答案2
[完全误导性的代码和声明被撤回]
[strike] 因此,诀窍在于,要输入到标记列表或宏中的文件必须以\endinput
. 结尾。[/strike] 对不起,我在这里出丑了。
我使用以下方法维护我的初步答案\read
:
(在下面的行中,将所有部分替换为,.toc
以便.som
更接近对 T.Verron 的回复)
几周前,当我开发自己的软件包时,尝试将文件放入标记列表时遇到了类似的问题\input
。因此,我现在这样做:.toc
\newtoks\my@toctoks
\def\my@readtoc#1{%
\ifeof #1
\let\my@nextread\@gobble
\global\my@toctoks=\expandafter{\the\my@toctoks}%
\else
\let\my@nextread\my@readtoc
\read #1 to \my@buffer
\my@toctoks=\expandafter\expandafter\expandafter
{\expandafter\the\expandafter\my@toctoks\my@buffer}%
\fi
\my@nextread{#1}}
\IfFileExists{\jobname .toc}
{{\endlinechar=-1 \makeatletter
\newread\my@tf
\openin\my@tf\@filef@und
\my@readtoc\my@tf
\closein\my@tf}}{}
这是为了读取.toc
文件(而不是立即排版)。请注意,我在我的包中放了,但这是出于某些特定原因。后果是最后\endlinechar=-1
不会有,而如果你不修改,TeX 在读取输入流时会生成一个。\par
\endlinechar
关于开始书写,您可以从命令内部获取代码\@starttoc
。如果您这样做,则其他包或宏将\begin{document}
无法再输入文件.toc
,因此最好只在第一次排版目录时这样做。
在我的软件包中,我也做了这些事情,\begin{document}
但是在最新版本(尚未在 CTAN 上)中,我.toc
按照上述方式读取文件,\usepackage
并在第一个 TOC 排版命令中打开进行写入。
答案3
我找到了一种方法,利用了这样一个事实:read
操作总是必须找到匹配的括号,并读取行直到括号匹配为止。诀窍就是用\jobname.som
括号开始文件,用括号结束文件。但是\write
也需要匹配的表达式,所以我们做了一些 catcode hackery。
\documentclass{article}
\newcommand{\sommaireline}[1]{\immediate\write\sommaire{#1}}
\begingroup
\catcode1=12
\catcode2=12
\AtBeginDocument{\newwrite\sommaire
\immediate\openout\sommaire=\jobname.som
\sommaireline{^^A}}
\AtEndDocument {\sommaireline{^^B}}
\catcode1=1
\catcode2=2
\IfFileExists{\jobname.som}%
{\newread\somfile
\openin\somfile=\jobname.som
\read\somfile to \mysomm
\expandafter\gdef\expandafter\mysommaire\mysomm}
{\gdef\mysommaire{No file \jobname.som}}
\endgroup
\begin{document}
X\sommaireline{aaaaa}
\mysommaire
\sommaireline{bbbbb}X
\end{document}