我编写了一小段代码(函数\getfirst:w
),在第一个嵌入处拆分其分隔参数:
。如果单独使用,该代码可以很好地工作,但我无法将其放入.code:n
键定义的中。\separatethis
执行键评估的文档命令()因错误而停止:
! Argument of \getfirst:w has an extra }.
出了什么问题?
\documentclass{article}
\usepackage{expl3}
\usepackage{xparse}
\ExplSyntaxOn
\cs_new:Npn\getfirst:w#1:#2\q_stop{#1}
\keys_define:nn{mypkg}{
myopt .code:n = {
\tl_set:Nx\myarg_tl{#1}
\tl_set:Nx\first_tl{\exp_after:wN\getfirst:w\myarg_tl\q_stop}
}
}
\NewDocumentCommand\separatethis{O{}}{
\keys_set:nn{mypkg}{#1}
\tl_use:N\first_tl
}
\ExplSyntaxOff
\begin{document}
%works
\ExplSyntaxOn
\tl_set:Nn\myarg_tl{foo:bar}
\tl_set:Nx\first_tl{\exp_after:wN\getfirst:w\myarg_tl\q_stop}
\tl_use:N\first_tl
\ExplSyntaxOff
%doesn't work
\separatethis[myopt=foo:bar]
\end{document}
答案1
问题在于 catcode,就像:
代码块内的“字母”一样。您需要使用将字符转换为字符串的方法
\documentclass{article}
\usepackage{expl3}
\usepackage{xparse}
\ExplSyntaxOn
\use:x
{
\cs_new:Npn \exp_not:N \getfirst:w
##1 \token_to_str:N : ##2 \exp_not:N \q_stop
{##1}
}
\keys_define:nn { mypkg }
{
myopt .code:n =
{ \tl_set:No \l_first_tl { \getfirst:w #1 \q_stop } }
}
\NewDocumentCommand { \separatethis } { O { } }
{
\keys_set:nn { mypkg } {#1}
\tl_use:N\l_first_tl
}
\ExplSyntaxOff
\begin{document}
\separatethis[myopt=foo:bar]
\end{document}
在旧版本的答案中,我建议使用传统 TeX 编程中的方法:使用替代字母并使用大小写更改来获得正确的内容。
\documentclass{article}
\usepackage{expl3}
\usepackage{xparse}
\ExplSyntaxOn
\group_begin:
\char_set_lccode:nn { `\+ } { `\: }
\tex_lowercase:D
{
\group_end:
\cs_new:Npn \getfirst:w #1 + #2 \q_stop {#1}
}
\keys_define:nn { mypkg }
{
myopt .code:n =
{ \tl_set:No \l_first_tl { \getfirst:w #1 \q_stop } }
}
\NewDocumentCommand { \separatethis } { O { } }
{
\keys_set:nn { mypkg } {#1}
\tl_use:N\l_first_tl
}
\ExplSyntaxOff
\begin{document}
\separatethis[myopt=foo:bar]
\end{document}
团队强烈反对在新代码中采用这种方法:我们正在删除直接接口\tex_lowercase:D
(因此需要使用:D
上面的版本),因为这种代码与大小写更改无关!
答案2
一种不同的方法可能是使用正则表达式替换:
\documentclass{article}
\usepackage{expl3,l3regex}
\usepackage{xparse}
\ExplSyntaxOn
\tl_new:N \l_alexg_first_tl
\keys_define:nn { mypkg }
{
myopt .code:n =
{
\tl_set:Nn \l_alexg_first_tl { #1 }
\regex_replace_once:nnN { (.*?) \: .* } { \1 } \l_alexg_first_tl
}
}
\NewDocumentCommand { \separatethis } { O { } }
{
\keys_set:nn { mypkg } {#1}
\tl_show:N \l_alexg_first_tl
}
\ExplSyntaxOff
\begin{document}
\separatethis[myopt=foo:bar]
\end{document}
这将回答
> \l_alexg_first_tl=macro:
->foo.
您基本上会保留所有标记,直到找到第一个冒号(类别代码 12),其余的标记将被丢弃。LaTeX3 正则表达式与 POSIX 正则表达式略有不同,但这些正是您在 Perl 中编写的用于执行相同操作的表达式。“搜索字符串”
(.*?)\:
匹配所有字符(实际上是标记),直到找到第一个冒号(由于“非贪婪”量词而找到第一个冒号*?
);由于括号,匹配到冒号的标记会被记住
\1
`.* 匹配搜索所适用的标记列表中的所有标记。
特别是整个标记列表与“搜索字符串”匹配。
“替换”字符串只是用已记住的内容替换所有已匹配的内容\1
。