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}