![例子](https://linux22.com/image/365473/%E4%BE%8B%E5%AD%90.png)
当将需要在下一次运行调用时扩展的内容写入辅助文件时\begin{document}
,必须取消对这些内容的扩展。这可以通过在活动字符 ( \
)前加上
\string
\protect
因此,当将宏写入\saveForLater
aux 文件时,我可以执行以下操作:
\immediate\write\@mainaux{\string\gdef\string\saveForLater{123}}
但是当我想使用语法将相同的宏(控制序列)写入辅助文件时\csname
,我必须使用\noexpand
\immediate\write\@mainaux{\string\expandafter\gdef\noexpand\csname saveForLater@\somedef\string\endcsname{456}}
\string\csname
不起作用,但\string
在 上运行良好(并且是必需的)\endcsname
。
请解释一下。
例子
\documentclass{article}
\makeatletter
\def\saveForLater{}%init
\def\somedef{HEY}
\begin{document}
\immediate\write\@mainaux{\string\gdef\string\saveForLater{123}}
\immediate\write\@mainaux{\string\expandafter\gdef\string\csname saveForLater@\somedef\string\endcsname{456}} % DOES NOT WORK
\immediate\write\@mainaux{\string\expandafter\gdef\noexpand\csname saveForLater@\somedef\string\endcsname{456}} % WORKS
\end{document}
取消注释该行,然后看
! Undefined control sequence.
l.3 \expandafter\gdef \csnamesaveForLater@HEY
\endcsname{456}
未注释所有内容的辅助文件
(并因错误而强制退出)
\relax
\gdef\saveForLater{123}
\expandafter\gdef \csnamesaveForLater@HEY\endcsname{456}
\expandafter\gdef \csname saveForLater@HEY\endcsname{456}
答案1
回想一下,\write
会完成扩展,直到剩余不可扩展的标记(当实际发生写入操作时)。由于\gdef
是不可扩展的,因此它前面不需要加\string
,除非您想避免其后有空格。
您的代码存在时间问题(让我省略\immediate
这里不相关的内容):
\write\@mainaux{\string\expandafter\gdef\noexpand\csname saveForLater@\somedef\string\endcsname{456}}
- 该标记
\string
是可扩展的,因此它作用于以下标记(\expandafter
) \gdef
不可扩展\noexpand
是可扩展的,因此它作用于下一个标记(\csname
)- 中的字符
saveForLater@
不可展开 \somedef
应该扩展为字符标记,假设它扩展为foo
\string
是可扩展的,因此它作用于下一个标记(\endcsname
)- 中的字符
{456}
不可扩展。
因此你得到的是
\expandafter\gdef \csname saveForLater@foo\endcsname{456}
你\string\csname
会得到
\expandafter\gdef \csnamesaveForLater@foo\endcsname{456}
因为有不\csname
传递给 的标记列表中后面跟着的空格\write
。
为什么后面会有\noexpand
? 原因与 后面会有 相同\gdef
:TeX 在执行写入操作时,会在不可扩展的控制字后添加一个空格。 使用 ,\noexpand\csname
通常可扩展的\csname
会暂时等同于,因此它会在后面带有一个空格,就像would (或)\relax
一样。\relax
\gdef
\immediate\write\@auxout{%
\gdef\expandafter\noexpand\csname saveForLater@\somedef\endcsname{456}%
}
将会写
\gdef \saveForLater@foo {456}
因为导致之前\expandafter
形成的控制序列可以作用于它。\csname
\noexpand
如果要保留文件\csname
中的内容.aux
:
\immediate\write\@auxout{%
\noexpand\expandafter
\gdef\noexpand\csname saveForLater@\somedef\endcsname{456}%
}
那将会写
\expandafter \gdef \csname saveForLater@foo\endcsname {456}
答案2
\immediate\write\@mainaux{\gdef \string\saveForLater{123}}
这应该可以工作,因为它保存\gdef\saveForLater{123}
在 .aux 中。
\immediate\write\@mainaux{\string\expandafter\gdef\string\csname saveForLater\string\endcsname{456}}
问题在于你保存\expandafter\gdef \csnamesaveForLater\endcsname{456}
在 .aux 中,因为\string
在输出控制序列名称后没有添加空格。所以当你再次阅读并看到\csnamesaveForLater
TeX 不知道那是什么时。你需要\string\csname\space
。但我不知道你的意图是什么,更简单似乎
\immediate\write\@mainaux{\gdef\expandafter\string\csname saveForLater\endcsname{456}}
这样就节省了\gdef\saveForLater{456}
。如果这不是您想要的,那么您需要它\space
。
或者,正如您已经看到的,使用\noexpand
。