我想学习 Plain TeX,我正在阅读 Knuth 的《The TeXBook》。在第 7 章中,Knuth 讨论了该\string
命令以及它如何将控制序列转换为字符标记列表。
为什么我打字时 \string\TeX
得到"TeX
,但当我写时却{\tt \string\TeX}
得到\TeX
?
我猜想这与有关\escapechar
,因为当我改变该控制序列时,输出也发生了变化:
\escapechar=`^
\string\TeX
它产生^TeX
。
答案1
的 ASCII 码\
是 92,这也是 TeX 反斜杠的内部代码。现在看一下 TeXbook 的附录 F(F就像字体表一样)。在那里你可以看到打字机字体cmtt10
确实\
在位置 92 有一个位置(八进制表示为 ´134),而标准文本字体在该位置cmr10
有一个引号“
。后者的原因是,在标准文本中,你经常需要引号,但几乎不需要反斜杠。
如果你想自己制作字体表,请使用以下代码(感谢史蒂芬·柯特维茨谢谢您的建议!):
\documentclass{article}
\addtolength{\textheight}{1cm}
\usepackage{fonttable}
\begin{document}
\fonttable{cmr10}
\fonttable{cmtt10}
\end{document}
答案2
Hendrik 的回答很棒。不过,请允许我\string
再详细阐述\escapechar
一下:
就像您所说的那样,该\string
原语将控制序列转换为字符标记列表1。它还适用于其他标记并将它们转换为字符串表示形式。这里有两点值得注意:除空格外的所有字符都返回为类别代码(catcode) 12“其他”,即使它们之前来自 catcode 11“字母”。如果控制序列包含空格(可能包含\csname .. \endcsname
),则它们仍然具有 catcode 10“空格”。此 catcode 更改对这些字符的排版或 ing 没有影响\write
。
其次,TeX 不会将控制序列中的反斜杠与每个控制序列名称一起存储。反斜杠仅在将控制序列标记为一个时才需要,一旦该控制序列被标记化,就不再需要它了。而且反斜杠并不是硬连线到 TeX 中的。每个带有 catcode 0“转义字符”的字符都可以使用。但是默认情况下,反斜杠是唯一一个具有此 catcode 的字符。由于 TeX 不会存储用于每个特定控制序列的转义字符,因此当它必须将控制序列重新转换为字符串时,它需要被告知如何表示它。这是使用\escapechar
寄存器2来完成的,该寄存器保存所用字符的 ASCII 编号。默认情况下,它是 92,这是反斜杠的编号。这个寄存器可以随意更改。如果它包含负数,则根本不会产生任何转义字符\string
。这个事实经常被 (La)TeX 代码使用,它喜欢只获取宏名称。Hendrik 是否已经在他的精彩回答中写道,您需要确保当前使用的字体中存在转义字符。通常,使用 tt 字体是正确的。
因此,例如\escapechar=`\A\catcode`\|=0 |string|foo
将输出Afoo
,而不是\foo
或|foo
。在 catcode 更改之后,TeX 不会在这里关心您是否使用|
或\
,您可以根据需要混合它们(请注意,现在\\
可以写成 ,|\
但不能写成 ,||
因为第二个反斜杠不是转义字符)。您也不必强制\escapechar
使用 catcode 0 的字符。
1)Knuth,TeXBook,第 40 页,第 7 章第 1 段:TEX 如何读取你输入的内容.2
) Knuth,TeXBook,第 40 页,第 7 章第 6 段:TEX 如何读取你输入的内容。
答案3
理论
它是如何\string
工作的?有各种各样的情况,但首先我们必须意识到,这是一个 TeX 的原始命令,它只查看下一个标记无需扩大. 此查找发生后标记化过程,可以以各种方式转换输入。
因此,我们感兴趣的是\string<token>
如果
<token>
是除 10 之外的任意类别代码的字符(包括活动字符),则扩展与类别代码 12 相同。但请注意, 不能<token>
属于类别 0、9、14 或 15,因为这样的字符永远不会到达发生扩展的“口”。如果
<token>
是类别代码 10 的字符,则扩展为 (32,10) 对,即类别代码 10 的普通空格标记。如果
<token>
是代码为 32 且具有任意类别代码的字符,则扩展名为 (32,10) 对,与前面的情况相同。如果
<token>
是控制序列,则 的扩展\string<token>
是控制序列的名称,前面是字符代码等于 的当前值的字符\escapechar
。此扩展中的所有字符都将具有类别代码 12,但空格除外,这些空格被规范化为字符代码 32 和类别代码 10 的正常空格标记。控制序列不需要定义,也不会存储在哈希表中。但是,如果 的值
\escapechar
小于 0 或大于 255,则返回的字符串开头不会添加任何字符。0x1FFFFF
对于 XeTeX 和 LuaTeX,上限为 2097151,即 。
例子
输入
\edef\x{\string a}\ifcat\x a\message{11}\else\message{12}\fi
将返回 12
输入
\catcode`?=10 \def\temp{?}\edef\x{\expandafter\string\temp}\show\x
将返回
> \x=macro: -> .
输入
\catcode`\ =12 \edef\x{\string }% \ifcat\x\space\message{10}\else\message{12}\fi
将返回 10
输入
\edef\x{\string\foo}\show\x
将返回
> \x=macro: ->\foo.
o
(和之间缺少空格.
表明这\foo
不是控制序列)。相反\escapechar=`A \edef\x{\string\foo}\show\x
将返回> Ax=macro: ->Afoo.
输入
\escapechar=-1 \edef\x{\string\foo}\show\x
将返回
> \x=macro: ->foo.
( 之前没有任何字符
foo
)
印刷
关于 的打印输出的问题\string\foo
则是另一回事;如果说\char`\\
,TeX 将打印当前字体的 92 号位置中的任何字符,该字符可能是反斜杠,也可能不是。它不在标准的 Plain TeX 字体 中\tenrm
,cmr10
而是在 中cmtt10
。打印 产生的字符串时也会发生同样的情况\string\foo
。
答案4
这里提到的问题与\string
TeX 原始行为无关,但与以下事实有关:Computer modern 文本字体不遵守 ASCII 编码(打字机 Computer Modern 字体除外,它们遵守 ASCII)。ASCII 的例外情况有 7 个位置:
<
打印为 ¡>
打印为 ¿\
打印为“{
打印为 –|
打印为 -}
打印为“$
在字体的草书变体中打印为 £
此外,字符\
、{
、}
、$
在纯 TeX 中(以及其他格式中)都有特殊的类别代码,因此如果您想打印它们,不能简单地写下来。
在这七次出现中不尊重 ASCII 的原因是:使用的 7 位(128 个插槽)Computer Modern 字体中的插槽很少,并且需要实现 ASCII 表中未包含的常见印刷字符。为什么 Knuth 不使用 8 位字体(256 个插槽)?它们在 TeX 内存中占用更多空间,并且 Knuth 需要预加载来自 Computer Modern 系列的许多字体。在 TeX 诞生的那些日子里,计算机的内存非常有限。人们认为<
、|
和>
只会在数学模式下使用(其中字体和插槽选择由与文本字体不同的原则完成),并且应该使用特殊的宏来打印 \ 、 { 、 } 、 $ 。
现在,这个问题只属于 TeX 历史,因为我们通常不使用 Computer Modern 字体,而是使用 Unicode 字体(1114111 个可用插槽)。如果有人遇到此处讨论的这个问题,则意味着他/她沉浸在历史软件(40 年前)的研究中,而不使用当前的现代 TeX。