替换 csv 列表中的条目

替换 csv 列表中的条目

我想替换 csv 列表中的条目,其中列表解析器可以随意的(例如:),;:|。示例如下:

\def\x{r;s;t;x;y;z}

在此列表中,我想r,t,x通过a,b,c以下方式有效地替换:

\replaceentries\x{r;s;t}{a;b;c}

但我也可能得到不平衡的配对,比如

\replaceentries\x{r;s;t}{a;b;c;d;e}

我不想TeX抱怨。

请问有人之前解决过这个问题吗,或者可能在软件包中找到了解决方案?否则,我想我可以尝试一下,但为什么要重新发明轮子呢?

答案1

xstring 包提供以下功能:

\documentclass{minimal}
\usepackage[T1]{fontenc}
\usepackage{xstring}
\begin{document}
\def\x{r;s;t;x;y;z}
\StrSubstitute\x{r;s;t}{a;b;c}[\x]
\meaning\x

\def\x{r;s;t;x;y;z}
\StrSubstitute\x{r;s;t}{a;b;c;d;e;f;g}[\x]
\meaning\x
\end{document}

[编辑]:抱歉,Ahmed,我误解了你到底想要什么!我希望这个代码更好:

\documentclass{minimal}
\usepackage[T1]{fontenc}
\usepackage{xstring}
\newcommand\replaceentries[4][;]{%
    \def\listremaining{#3#1}\def\substremaining{#4#1}%
    \saveexpandmode\expandarg
    \loop
    \unless\ifx\empty\listremaining
        \StrBefore\listremaining{\noexpand#1}[\currentitem]%
        \StrBehind\listremaining{\noexpand#1}[\listremaining]%
        \StrBefore\substremaining{\noexpand#1}[\currentsubst]%
        \StrBehind\substremaining{\noexpand#1}[\substremaining]%
        \StrSubstitute#2\currentitem\currentsubst[#2]%
    \repeat
    \restoreexpandmode}
\begin{document}
\def\x{r;s;t;x;y;z}
\replaceentries\x{x;y;z}{a;b;c}
\meaning\x

\def\x{r!s!t!x!y!z}
\replaceentries[!]\x{t!x!z}{A!B!C!D!E}
\meaning\x
\end{document}

答案2

为什么不直接用任何包来做呢?代码并不复杂:

\documentclass{minimal}
\usepackage[T1]{fontenc}
\makeatletter
\newcommand\if@instr[2]{%
    \def\if@instr@i##1#2##2\@nil{\ifx\if@instr@i##2\expandafter\@secondoftwo\else\expandafter\@firstoftwo\fi}%
    \expandafter\if@instr@i#1\@@nil#2\if@instr@i\@nil}
\newcommand\subst@[3]{%
    \def\subst@i##1#2##2\@nil{\def#1{##1#3##2}\subst@#1{#2}{#3}}%
    \if@instr#1{#2}{\expandafter\subst@i#1\@nil}\relax}
\newcommand\split@at[4]{%
    \def\split@at@i##1#2##2\@nil{\def#3{##1}\def#4{##2}}%
    \expandafter\split@at@i#1\@nil}
\newcommand\replaceentries[4][;]{%
    \def\listremaining{#3#1}\def\substremaining{#4#1}%
    \loop
    \unless\ifx\empty\listremaining
    \split@at\listremaining{#1}\currentitem\listremaining
    \split@at\substremaining{#1}\currentsubst\substremaining
    \expandafter\expandafter\expandafter\subst@
    \expandafter\expandafter\expandafter#2%
    \expandafter\expandafter\expandafter{\expandafter\currentitem\expandafter}\expandafter{\currentsubst}%
    \repeat}
\makeatother
\begin{document}
\def\x{a;b;c;d;e;f;g;h}
\replaceentries\x{a;c;f}{J;K;L}
\x

\def\x{r!s!t!x!y!z}
\replaceentries[!]\x{t!x!z}{A!B!C!D!E}
\x
\end{document}

答案3

unbonpetit 的回答看起来简短而漂亮(非常感谢您的努力!),但这是因为xstring在后台。与此同时,我获得了一个基于catoptions包的 API 的解决方案。它首先规范化列表。抱歉,命令可能不熟悉。

\robust@def*\replaceoneelement{\cpt@testopt\@replaceoneelement{,}}
\robust@def\@replaceoneelement[#1]#2#3#4{%
  \if@checklp\cpt@checklistparser{#1}\relax\fi
  \begingroup
  \def\@tempa##1{%
    \def\@replace####1#1##1#1####2\cpt@nil{\def#2{####1#1#4#1####2}}%
    \edef\@tempa{%
      {\detokenize{#1##1#1}}{\detokenize{#1}\cptoxdetok{#2}\detokenize{#1}}}%
    \expandafter\xifinsetFT\@tempa{}{%
      \expandafter\@replace\expandafter#1#2#1\cpt@nil
      \trimoneparser{#1}{#2}%
    }%
  }%
  \if@twincall
    \edef\@tempb{\unexpanded{#3}}%
  \else
    \@csvnormalize#1#2%
    \edef\@tempb{\xp@despace{#3}}%
  \fi
  \expandafter\@tempa\expandafter{\@tempb}%
  \exitgroupdef#2%
}
\robust@def*\replaceelements{\cpt@testopt\@replaceelements{,}}
\robust@def\@replaceelements[#1]#2#3#4{%
  \begingroup
  \cpt@csvnormalize#1#2%
  \edef\@tempa{\unexpanded{#3}}%
  \cpt@csvnormalize#1\@tempa
  \edef\@tempb{\unexpanded{#4}}%
  \cpt@csvnormalize#1\@tempb
  \def\@tempc##1#1##2\cpt@unique##3#1##4\cpt@unique{%
    \@replaceoneelement[#1]#2{##1}{##3}%
    \ifblankTF{##2}{}{\ifblankTF{##4}{}{\@tempc##2\cpt@unique##4\cpt@unique}}%
  }%
  \@checklpfalse\@twincalltrue
  \cptexpandsecond\@tempc{%
    \expandcsonce\@tempa#1\cpt@unique\expandcsonce\@tempb#1\cpt@unique
  }%
  \exitgroupdef#2%
}

答案4

这是 unbonpetit 最新解决方案的变体:

\usepackage{catoptions}
\makeatletter

\newif\ifsubstall
\newcommand\subst@[3]{%
  % Substitute all occurrences of #2 in #1 by #3.
  % I didn't need this, but in general it is important.
  % There is, however, need for a guard, in case we
  % don't need to substitute all the occurrences.
  \def\subst@i##1#2##2\@nil{%
    \def#1{##1#3##2}%
    \ifboolFT{substall}{}{\subst@#1{#2}{#3}}%
  }%
  \oifinsetFT{#2}{#1}{}{\expandafter\subst@i#1\@nil}%
}
\newcommand\split@at[3]{%
  \def\split@at@i##1#3##2\@@nil{\def#2{##1}\def#1{##2}}%
  \expandafter\split@at@i#1\@@nil
}
\newcommand\replaceentries[4][;]{%
  \def\listremaining{#3#1}\def\substremaining{#4#1}%
  \@tempswatrue
  % \substremaining may run out before \listremaining. Hence
  \loop
  \ifx\@empty\listremaining\@tempswafalse\else
    \ifx\@empty\substremaining\@tempswafalse\fi\fi
  \if@tempswa
  \split@at\listremaining\currentitem{#1}%
  \split@at\substremaining\currentsubst{#1}%
  % We can avoid a chain of \expandafters here:
  \cptexpandtwoargsonce{\subst@#2}\currentitem\currentsubst
  \repeat
}

\makeatother
\begin{document}
\substalltrue
\def\x{a;b;c;d;e;a;f;g;h}
\replaceentries\x{a;c;f}{A;C;F}
\x

\def\x{r!s!t!x!y!r!z}
\replaceentries[!]\x{r!x!z}{R!X!Z!D!E}
\x
\end{document}

相关内容