StrSubstitute for multiple replacements in a loop?
The problem is that each cycle of \foreach
is performed inside a group. So you have to globally save \MyText
after the substitution; however a straightforward
\documentclass[11pt]{article}
\usepackage{etoolbox}
\usepackage{xstring}
\usepackage{tikz}
\begin{document}
\def\KeyWords{one, two, three}
\def\MyText{This is one, followed by two later by three}
\expandarg
Original Text: \MyText\\
\foreach \keyword in \KeyWords
{
Replacing: \texttt{\keyword}
\StrSubstitute{\MyText}{\keyword}{\textbf{\keyword}}[\temp]%
\global\let\MyText\temp
Now MyText: \MyText \\
}
Final: \MyText
\end{document}
will not work, because the keywords are replaced by \textbf{\keyword}
, where \keyword
doesn't get expanded.
You have to expand \keyword
in the substitution string; for instance with
\documentclass[11pt]{article}
\usepackage{etoolbox}
\usepackage{xstring}
\usepackage{tikz}
\begin{document}
\def\KeyWords{one, two, three}
\def\MyText{This is one, followed by two later by three}
\expandarg
Original Text: \MyText
\foreach \keyword in \KeyWords
{
Replacing: \texttt{\keyword}
\begingroup\edef\x{\endgroup
\unexpanded{\StrSubstitute{\MyText}{\keyword}}
{\noexpand\textbf{\keyword}}}\x[\temp]
\global\let\MyText\temp
Now MyText: \MyText \\
}
Final: \MyText
\end{document}
I's suggest a different approach with l3regex
:
\documentclass{article}
\usepackage{xparse,l3regex}
\ExplSyntaxOn
\NewDocumentCommand{\setkeywords}{m}
{
\gopa_set_keywords:n { #1 }
}
\NewDocumentCommand{\changekeywords}{ O{textbf} m}
{
\gopa_change_keywords:nn { #1 } { #2 }
}
\cs_new_protected:Npn \gopa_set_keywords:n #1
{
\seq_set_split:Nnn \l_gopa_keywords_seq { , } { #1 }
\tl_set:Nx \l_gopa_keywords_tl { \seq_use:Nn \l_gopa_keywords_seq { | } }
\tl_put_left:Nn \l_gopa_keywords_tl { \b( }
\tl_put_right:Nn \l_gopa_keywords_tl { ) }
\regex_gset:NV \g_gopa_keywords_regex \l_gopa_keywords_tl
}
\cs_new_protected:Npn \gopa_change_keywords:nn #1 #2
{
\tl_set:Nn \l_gopa_sentence_tl { #2 }
\regex_replace_all:NnN \g_gopa_keywords_regex { \c{#1}\cB\{\1\cE\} } \l_gopa_sentence_tl
\tl_use:N \l_gopa_sentence_tl
}
\tl_new:N \l_gopa_keywords_tl
\tl_new:N \l_gopa_sentence_tl
\seq_new:N \l_gopa_keywords_seq
\regex_new:N \g_gopa_keywords_regex
\cs_generate_variant:Nn \regex_gset:Nn { NV }
\ExplSyntaxOff
\setkeywords{one, two, three}
\begin{document}
\changekeywords{This is one, followed by two later by three}
\changekeywords[textit]{This is one, followed by two later by three}
\end{document}
The \setkeywords
command defines the keywords to change; with `\changekeywords you specify the text and, optionally, the format to use (only the control sequence name, rather than a command).
How does it work? From the list of keywords, we prepare a regular expression in the form
\b ( one | two | three )
is built. Matches are replaced by \textbf{\1}
where \1
represents the matching string.
Here's an alternate way, using stringstrings
. Downside: its a slow package.
\documentclass[12pt]{article}
\usepackage{stringstrings}
\newcounter{index}
\newcommand\emboldenkeywords[2]{%
\getargs{#1}%
\setcounter{index}{0}%
\edef\thestring{#2}%
\encodetoken[1]{\bfseries}
\encodetoken[2]{\mdseries}
\whiledo{\value{index} < \narg}{%
\stepcounter{index}%
\edef\nextkeyword{\csname arg\roman{index}\endcsname}%
\convertword[e]{\thestring}{\nextkeyword}{\bfseries\nextkeyword\mdseries}%
}%
\retokenize{\thestring}%
\thestring%
\decodetoken[1]{\bfseries}
\decodetoken[2]{\mdseries}
}
\begin{document}
\emboldenkeywords{one two three}{This is one, followed by two later by three}
\end{document}