拆分分隔标记列表参数

拆分分隔标记列表参数

我编写了一小段代码(函数\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. (.*?)\:匹配所有字符(实际上是标记),直到找到第一个冒号(由于“非贪婪”量词而找到第一个冒号*?);

  2. 由于括号,匹配到冒号的标记会被记住\1

  3. `.* 匹配搜索所适用的标记列表中的所有标记。

特别是整个标记列表与“搜索字符串”匹配。

“替换”字符串只是用已记住的内容替换所有已匹配的内容\1

相关内容