在压缩文本.tex建议使用以下方案来测试字符串是否包含 catcode 为 6 的参数字符。
\def\endcheck{\endcheck}
\def\second#1#2{#2}
\def\gobbletocheck#1\endcheck#2#3{#2}
\def\checkhash#1{\docheckhash#1\endcheck}
\def\docheckhash#1{%
\ifx#1\endcheck
\expandafter\second
\else
\ifx#1##%
\expandafter\expandafter\expandafter\gobbletocheck
\else
\expandafter\expandafter\expandafter\docheckhash
\fi
\fi
}
这可以称为
\checkhash{abc#def}{YES}{NO}
\checkhash{abcdef}{YES}{NO}
显然,正如作者所建议的那样,如果要测试的字符串包含前两个标记相同的括号组,则此测试失败,例如,
\checkhash{ab{cc}def}{YES}{NO}
请问有人能建议一个更好的测试吗?请注意,哈希字符的 catcode 必须是 6,而不是 12。
可扩展的解决方案很漂亮但不是必需的。
答案1
这很容易,但不可扩展:
\documentclass{article}
\usepackage{xparse,l3regex}
\ExplSyntaxOn
\NewDocumentCommand{\checkhash}{mmm}
{
\musa_checkhash:nnn { #1 } { #2 } { #3 }
}
\cs_new_protected:Npn \musa_checkhash:nnn #1 #2 #3
{
\regex_match:nnTF { \cP. } { #1 } { #2 } { #3 }
}
\ExplSyntaxOff
\begin{document}
\checkhash{abc#def}{YES}{NO}
\checkhash{abcdef}{YES}{NO}
\checkhash{ab{cc}def}{YES}{NO}
\checkhash{ab{c#c}def}{YES}{NO}
\end{document}
输出为
是
否
否
是
l3regex
(LaTeX3 实验版) 软件包的模块提供expl3
正则表达式搜索和替换。除了大部分常见的正则表达式机制外,它还允许处理类别代码。字符、字符范围或通配符前面可以加一个前缀,说明我们想要的类别代码(这些前缀既可用于搜索,也可用于替换文本)。前缀包括
\cC \cB \cE \cM \cT \cP \cU \cD \cS \cL \cO \cA
分别对应于控制序列、开始组(catcode 1)、结束组(catcode 2)、数学移位(catcode 3)、对齐(catcode 4)、参数(catcode 6)、上标(catcode 7)、下标(catcode 8)、空格(catcode 10)、字母(catcode 11)、其他字符(catcode 12)、活动字符(catcode 13)。缺失的代码永远无法到达 TeX 口中的标记,因此它们没有前缀(转义,0;行尾,5;忽略,9;注释,14;无效,15)。还有用于\cC
表示控制序列的。
我们在代码中检查任何具有类别代码 6 的字符出现在作为参数给出的标记列表中:句点是表示任何字符(实际上是标记,在中)的通配符l3regex
;它前面带有 表示\cP
只有具有类别代码 6 的字符才会匹配。
如果只考虑#
6 个字符,则要使用的正则表达式为
\cP\#
请注意,正则表达式中的空格会被忽略;使用\
(即反斜杠和空格)来表示正则表达式中的空格。
答案2
如上所述的测试
\ifx#1\endcheck
如果的前两个标记#1
相等,则为真。这部分很容易修复。使用\ifx
测试时,始终将守卫标记放在第一位
\ifx\endcheck#1
现在,如果输入令牌,您只会得到不好的结果,\endcheck
您可以通过使用更难输入的令牌作为保护令牌来降低这种情况发生的可能性。
因此,这是一个可扩展的测试,删除了两级括号组(虽然可以扩展,但不能再多了)
\def\endcheck{\endcheck}
\def\second#1#2{#2}
\def\gobbletocheck#1\endcheck#2#3{#2}
\def\checkhash#1{\docheckhash#1\endcheck}
\def\firstofone#1{#1}
\def\docheckhash#1{\expandafter\xdocheckhash\firstofone#1}
\def\xdocheckhash#1{%
\ifx\endcheck #1%
\expandafter\second
\else
\ifx###1%
\expandafter\expandafter\expandafter\gobbletocheck
\else
\expandafter\expandafter\expandafter\docheckhash
\fi
\fi
}
这可以称为
\checkhash{abc#def}{YES}{NO}
\checkhash{abcdef}{YES}{NO}
\checkhash{ab{cc}def}{YES}{NO}
\checkhash{ab{#d}f}{YES}{NO}
\checkhash{ab{{#d}}f}{YES}{NO}
\bye
本报告 是 否 否 是 是
答案3
使用 的可扩展解决方案expl3
,它可以在任何嵌套级别找到显式和隐式 catcode 6 个标记:
\documentclass{article}
\usepackage{expl3}
\ExplSyntaxOn
\prg_new_conditional:Npnn \musa_if_param:n #1 { TF }
{ \__musa_if_param:n { #1 ? } }
\cs_new:Npn \__musa_if_param:n #1
{
\tl_if_empty:nTF {#1} { \prg_return_false: }
{
\tl_if_head_is_N_type:nTF {#1}
{
\tl_if_head_eq_catcode:nNTF {#1} ##
{ \prg_return_true: }
{ \exp_args:No \__musa_if_param:n { \use_none:n #1 } }
}
{ \exp_args:No \__musa_if_param:n { \use:n #1 } }
}
}
\musa_if_param:nTF { 123 ~ \foobar } { \error } { }
\musa_if_param:nTF { 12 { { 3 } 4 # } } { } { \error }
\ExplSyntaxOff
\stop
答案4
我们可以通过以下可扩展方案安全地探索无限嵌套的括号群。
\makeatletter
\begingroup
\lccode`\&=1 \catcode`\&=7
\lccode`\*=1 \catcode`\*=11
\lccode`\?=1 \catcode`\?=8
\lowercase{\endgroup
\gdef\if@func#1#2{\if@func@i#1x?#2*?}
\gdef\if@func@i#1#2?#3#4?{%
\csname @\ifx#1#3first\else second\fi oftwo\endcsname
}
\gdef\if@blank#1{%
\csname @\ifcat?\detokenize\expandafter{\@gobble#1.}?%
first\else second\fi oftwo\endcsname
}
\gdef\gobbletoendhashcheck#1#3{#2}
\gdef\checkforhash#1{\docheckhash@i#1&}
\gdef\docheckhash@i#1{%
\if@blank{#1}{%
\docheckhash@i
}{%
\expandafter\expandafter\expandafter\docheckhash@ii\expandafter
\@cdr\detokenize{#1}\@nil?{#1}%
}%
}
\gdef\docheckhash@ii#1?#2{%
\if@func{##}{#2}{%
\docheckhash@iii{##}%
}{%
\if@blank{#1}{\docheckhash@iii{#2}}{\docheckhash@i#2}%
}%
}
\gdef\docheckhash@iii#1{%
\if@func{&}{#1}
\@secondoftwo
{\if@func{##}{#1}\gobbletoendhashcheck\docheckhash@i}%
}
}
\makeatother
测试
\def\y#1{\edef\x{\checkforhash{#1}{Y}{N}}\typeout{\x}}
\y{a{}d}
\y{ab{{{{cc}}}}de}
\y{ab{{cc}}{{#}}de}