使用 xstring 从列表中选择

使用 xstring 从列表中选择

我想要一个执行以下操作的命令:

\variation{1}{aaa;bbb;ccc} ----> aaa
\variation{2}{aaa;bbb;ccc} ----> bbb
\variation{3}{aaa;bbb;ccc} ----> ccc

第二个输入中的字符串可以是包含任意多个 ; 的列表(可能根本没有),而不仅仅是如图所示的三个。我几乎已经使用 xstring 得到了我需要的东西,但它包含多余的输入和符号,基本上不够优雅!

\newcommand{\variation}[3]{\StrBetween[#1,#2]{;#3;}{;}{;}}

因此这就是我得到的

\variation{1}{2}{aaa;bbb;ccc} ----> aaa

第二个输入是多余的。

答案1

xstring和 eTeX 的解决方案\numexpr

\documentclass{article}

\usepackage{xstring}

\newcommand*{\variation}[2]{%
  \StrBetween[{#1},{\numexpr(#1)+1\relax}]{;#2;}{;}{;}%
}

\newcommand*{\test}[1]{%
  \detokenize{#1} $\rightarrow$ #1%
}

\begin{document}
\noindent
\ttfamily
\test{\variation{1}{aaa;bbb;ccc}}\\
\test{\variation{2}{aaa;bbb;ccc}}\\
\test{\variation{3}{aaa;bbb;ccc}}
\end{document}

结果

更新

  • \StrBetween 知道最后一个可选参数,允许将结果存储在宏中。因为我不知道如何\variation使用,所以宏继承了最后一个可选参数。在 LaTeX 中查找可选参数(基于\@ifnextchar)通常会删除后面的空格以找到开头的方括号。\relax停止查找并 保留[参数括号后的空格。\variation

  • 注释中的第二个问题:软件包xstring在数字比较后添加了空格(未注释的行尾)。如果是显式数字,则忽略空格并完成数字的解析。但如果数字由整数寄存器给出,或者在本例中由完整的给出\numexpr,则不需要空格并保留空格。

    有几种方法可以解决此问题:

    • 错误报告。
    • \the\numexpr代替\numexpr
    • 展开参数并转换为显式数字。然后\StrBetween只能看到展开的数字。

新的测试文件:

\documentclass{article}

\usepackage{xstring}

\newcommand*{\variation}[2]{%
  \edef\variationNext{%
    \noexpand\StrBetween[%
      \the\numexpr(#1)\relax,%
      \the\numexpr(#1)+1\relax
    ]%
  }%
  \variationNext{;#2;}{;}{;}\relax
}

\newcommand*{\test}[1]{%
  \detokenize\expandafter{\string#1} $\rightarrow$ [#1]%
}

\begin{document}
\noindent
\ttfamily
\test{\variation{1}{aaa;bbb;ccc}}\\
\test{\variation{2}{aaa;bbb;ccc}}\\
\test{\variation{3}{aaa;bbb;ccc}}
\end{document}

结果

答案2

这里有一个使用xparse/expl3而不是 的选项xstring(如果您只对解决方案感兴趣xstring,那么就将其作为比较)。该variation宏采用可选的第一个参数来确定要拆分列表的字符,默认值为;

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\msg_new:nnn {variation}{index~out~of~bounds}{You~have~requested~an~item~that~doesn't~exist}

\NewDocumentCommand{\variation}{ O{;} m m }
 {
    % split arg #3 at every occurrence of #1 (default #1=;) and store in sequence
    \seq_set_split:Nnn \l_tmpa_seq {#1}{#3}

    % compare arg #2 to the length of the sequence
    \int_compare:nTF {#2>\seq_count:N \l_tmpa_seq}

        % if arg #2 > sequence length, then the item doesn't exist
        {\msg_warning:nnn {variation}{index~out~of~bounds}{}}

        % if arg #2 <= sequence length, then return requested item.
        {\seq_item:Nn \l_tmpa_seq {#2}}
 }

\ExplSyntaxOff

\begin{document}

\variation{3}{aaa;bbb;ccc;ddd;eee}
\variation{1}{aaa}
\variation{2}{aaa}

\end{document}

答案3

这只是 Scott H. 解决方案的一个变体,用于展示一种更好的解决问题的方法。

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn

\NewDocumentCommand{\variation}{ s O{;} m m }
 {
  \IfBooleanTF{#1}
   {
    \geoff_variation_check:nnn { #2 } { #3 } { #4 }
   }
   {
    \geoff_variation_nocheck:nnn { #2 } { #3 } { #4 }
   }
 }

\msg_new:nnn { variation }
 { index~out~of~bounds }
 { You~have~requested~an~item~that~doesn't~exist }

\seq_new:N \l__geoff_item_list_seq

\cs_new_protected:Npn \geoff_variation_nocheck:nnn #1 #2 #3
 {
  \seq_set_split:Nnn \l__geoff_item_list_seq { #1 } { #3 }
  \seq_item:Nn \l__geoff_item_list_seq { #2 }
 }

\cs_new_protected:Npn \geoff_variation_check:nnn #1 #2 #3
 {
  \seq_set_split:Nnn \l__geoff_item_list_seq { #1 } { #3 }
  \int_compare:nTF { #2 > \seq_count:N \l__geoff_item_list_seq }
   { \msg_warning:nn { variation }{ index~out~of~bounds } }
   { \seq_item:Nn \l__geoff_item_list_seq {#2} }
 }

\ExplSyntaxOff

\begin{document}

\variation{3}{aaa;bbb;ccc;ddd;eee}

\variation[,]{3}{aaa,bbb,ccc,ddd,eee}

\variation{1}{aaa}

\variation{2}{aaa}

\variation*{2}{aaa}

\end{document}

指令\NewDocumentCommand应该用于将参数传递给“内部”函数。为了更好地说明如何操作,我引入了一个 *-variant,因此控制权要么传递给 ,要么\geoff_variation_check:nnn传递给\geoff_variation_nocheck:nnn。内部函数应该是protected,因为它们执行不可扩展的操作(\set...)。

大部分操作是将参数拆分为一个序列并返回所需的序列项:

\seq_set_split:Nnn <sequence> { <tokens> } { <token list> }

\seq_item:Nn <sequence> { <integer expression> }

答案4

在一般情况下,机器可能会生成如下列表

{aaa;bbb; ; ccc}

这将产生意想不到的结果

\test{\variation{3}{aaa;bbb; ; ccc}} -> [ ]

此外,解析器(;在本例中)可能处于活动状态。这些要求事先对列表进行规范化。

\documentclass{article}
\usepackage{catoptions}
\makeatletter
% \getstrofnumber{<parser>}{<nr>}{<list>}
\robust@def*\getstrofnumber#1#2#3{%
  \begingroup
  \@tempcnta\z@
  \def\@getstrofnumber##1#1##2\get@nnil{%
    \ifstrcmpTF{##1}\get@nil{%
      \endgroup
      \@latexerr{Item of serial number '#2' not found}\@ehd
    }{%
      \advance\@tempcnta\@ne
      \ifnum#2=\@tempcnta
        \endgroup##1%
        \expandafter\@gobble
      \else
        \expandafter\@iden
      \fi
      {\@getstrofnumber##2\get@nnil}%
    }%
  }%
  \edef\alist{\unexpanded{#3}}%
  \cpt@sttrue\cpt@csvnormalize[#1]\alist
  \expandafter\@getstrofnumber\alist#1\get@nil#1\get@nnil
}
\makeatother

\begin{document}
\def\pput#1{\char`{#1\char`} \space$\rightarrow$\space}
\noindent
\ttfamily
\pput{aaa;bbb; ; ccc}\getstrofnumber{;}{1}{aaa;bbb; ; ccc}\\
\pput{aaa;;bbb;ccc}\getstrofnumber{;}{2}{aaa;;bbb;ccc}\\
\pput{aaa;bbb;ccc}\getstrofnumber{;}{3}{aaa;bbb;ccc}\\
% \pput{aaa;bbb;ccc}\getstrofnumber{;}{4}{aaa;bbb;ccc}% error

\end{document}

在此处输入图片描述

相关内容