我想替换 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}