例子

例子

当将需要在下一次运行调用时扩展的内容写入辅助文件时\begin{document},必须取消对这些内容的扩展。这可以通过在活动字符 ( \)前加上

  1. \string
  2. \protect

因此,当将宏写入\saveForLateraux 文件时,我可以执行以下操作:

\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}}
  1. 该标记\string是可扩展的,因此它作用于以下标记(\expandafter
  2. \gdef不可扩展
  3. \noexpand是可扩展的,因此它作用于下一个标记(\csname
  4. 中的字符saveForLater@不可展开
  5. \somedef应该扩展为字符标记,假设它扩展为foo
  6. \string是可扩展的,因此它作用于下一个标记(\endcsname
  7. 中的字符{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在输出控制序列名称后没有添加空格。所以当你再次阅读并看到\csnamesaveForLaterTeX 不知道那是什么时。你需要\string\csname\space。但我不知道你的意图是什么,更简单似乎

\immediate\write\@mainaux{\gdef\expandafter\string\csname saveForLater\endcsname{456}}

这样就节省了\gdef\saveForLater{456}。如果这不是您想要的,那么您需要它\space

或者,正如您已经看到的,使用\noexpand

相关内容