假设我有一些宏(\iPrint
在 MWE 中),它在内部使用活动字符来解释其参数:
\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\begingroup
\lccode`\~=`\|
\lowercase{\endgroup
\def~#1~{\textbf{#1}}%
}
\def\iPrint{%
\begingroup
\catcode`\|=\active
\iPrintX
}
\def\iPrintX#1{%
#1
\endgroup
}
\begin{document}
\def\temp{This |doesn't work|, though :-(}
\begin{itemize}
\item \iPrint{This |works pretty| well!}
\item \iPrint{\temp}
\end{itemize}
\end{document}
得出:
据我所知,问题是,catcode 是在宏定义时分配的:\temp
定义时,|
不处于活动状态。但是,我是否可以在其中执行一些操作\iPrint
以使其处于活动状态?使用当前 catcode 对参数进行某种重新编码?
答案1
当你说
\def\temp{This |doesn't work|, though :-(}
的类别代码|
是固定的,\catcode`|=\active
在进行扩展时设置不起作用\temp
:|
替换文本中的字符具有类别代码 12。
可以用来\scantokens
重新分配类别代码,但必须扩展宏前 \scantokens
发挥作用:
\makeatletter
\begingroup
\lccode`\~=`\|
\lowercase{\endgroup
\def~#1~{\textbf{#1}}%
}
\def\iPrint#1{%
\begingroup
\catcode`\|=\active
\protected@edef\reserved@iprint{\noexpand\scantokens{#1\noexpand\empty}}\reserved@iprint
\endgroup
}
\makeatother
如果已知输入\iPrint
仅为文本或宏(例如\iPrint{\temp}
),那么更简单的
\def\iPrint#1{%
\begingroup
\catcode`\|=\active
\scantokens\expandafter{#1\empty}%
\endgroup}
可以工作。如果参数更复杂(文本和宏),则\protected@edef
似乎需要更复杂的方法;它将扩展所有宏,但不会深入研究“受保护”的宏。
\empty
参数末尾的是\scantokens
惯例,以避免出现虚假空格:\scantokens
实际上模拟文件的输入,并且所有文件都隐式以空行结束。\protected@edef
它里面不能扩展,所以我们\noexpand
在它前面添加。
(感谢 Bruno Le Floch 纠正错误。)
答案2
当读取输入字符时,会分配 catcode。TeX 随后会查找此输入的当前值并附加 catcode。之后无法更改。因此,如果要更改输入,必须重新读取输入。例如,将其写入文件,然后重新输入此文件。或者使用\scantokens
。
\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\begingroup
\lccode`\~=`\|
\lowercase{\endgroup
\def~#1~{\textbf{#1}}%
}
\def\iPrint{%
\begingroup
\catcode`\|=\active
\iPrintX
}
\def\iPrintX#1{%
#1
\endgroup
}
\begin{document}
\def\temp{This |doesn't work|, though :-(\relax}
\begin{itemize}
\item \iPrint{This |works pretty| well!}
\item \iPrint{\scantokens\expandafter{\temp}}.
\item \iPrint{\temp}.
\end{itemize}
\end{document}