将 \everyeof 和 \endlinechar 与 \scantokens 结合使用

将 \everyeof 和 \endlinechar 与 \scantokens 结合使用

e-TeX基元允许对输入进行重新标记。但是,它几乎总是用于设置和\scantokens的组中(例如,参见\everyeof\endlinechar我可以将字符串转换为 catcode 11 吗?)。要求这两个步骤的理由是什么?

答案1

e-TeX 手册中描述了该\scantokens原语,其工作方式与以下代码类似:

\toks0={...}% '...' is the rescanned material
\immediate\openout0=file
\immediate\write0{\the\toks0}
\immediate\closeout0
\input file

但不使用文件,并且采用可扩展的方式。但是,它确实使用了一些与上述相同的内部结构。这对使用原语有影响。

TeX 会“读取”一个伪文件,并将其视为具有文件结束 (EOF) 标记。\scantokens尝试将其读取为一个标记,但这会引发错误,例如

! File ended while scanning definition of \demo

带有代码

\edef\demo{\scantokens{foo}}

为了防止这种情况,您需要设置在此标记前\everyeof插入:\noexpand

\everyeof{\noexpand}
\edef\demo{\scantokens{foo}}

然后 TeX 不会尝试读取文件末尾的内容,从而避免了这个错误。

第二个问题是 TeX 在 中以常规方式标记“行尾”字符\scantokens。常见的用法是扫描单行,如上所示,但结果可能不如预期:

\everyeof{\noexpand}
\edef\demo{\scantokens{foo}}
\show\demo

产量

> \demo=macro:
->foo .

带有额外的空格:最后的“行尾”(伪文件的结尾)将转换为空格。为了防止这种情况,您通常使用以下方式更改行尾行为

\endlinechar=-1

这样行尾就会被忽略并且不会添加空格。

然后,将所有内容包装到一个组中是标准做法,例如在宏中保存结果时

\long\def\safescantokens#1#2{%
  \begingroup
    \everyeof{\noexpand}%
    \endlinechar=-1
    \xdef#1{\scantokens{#2}}%
  \endgroup
}
\safescantokens\demo{foo}

这里使用组是为了使两个附加步骤不会影响任何其他代码,而\xdef这是在组外获取结果的最简单方法。(适当的\expandafter链也是实现此目的的一种可能方法。)

所有这些都使得最终的使用不可扩展,这在某种程度上违背了原语的初衷(尽管文件仍然没有被使用)。因此,在 LuaTeX 中有一个\scantextokens原语专门解决这些问题:忽略文件结尾,并且在最后一行(几乎总是唯一的一行)后不插入结束行字符。

答案2

另一种并非完全不安全的定义是

\long\def\safescantokensb#1#2{%
    \scantokens{\def#1{#2}\ignorespaces}%
}
\safescantokensb\demo{foo}

相关内容