Command which replaces all occurrences of a certain character with another character?

Use expl3:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\myreplace}{m}
 {
  \tl_set:Nn \l__maxd_argument_tl { #1 }
  \tl_replace_all:Nnn \l__maxd_argument_tl { - } { . }
  \tl_use:N \l__maxd_argument_tl
 }
\tl_new:N \l__maxd_argument_tl
\ExplSyntaxOff

\begin{document}

\myreplace{1-2-abc-345}

\end{document}

enter image description here

What's the advantage? You can easily generalize your command using the same idea.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\myreplace}{ O{} m }
 {
  \maxd_replace:nn { #1 } { #2 }
 }

\tl_new:N \l__maxd_argument_tl
\cs_new_protected:Npn \maxd_replace:nn #1 #2
 {
  \group_begin:
  \tl_set:Nn \l__maxd_argument_tl { #2 }
  \keys_set:nn { maxd/replace } { #1 }
  \tl_replace_all:NVV \l__maxd_argument_tl \l_maxd_search_tl \l_maxd_replace_tl
  \tl_use:N \l__maxd_argument_tl
  \group_end:
 }
\cs_generate_variant:Nn \tl_replace_all:Nnn { NVV }

\keys_define:nn { maxd/replace }
 {
  search .tl_set:N = \l_maxd_search_tl,
  search .initial:n = { - },
  replace .tl_set:N = \l_maxd_replace_tl,
  replace .initial:n = { . }
 }

\ExplSyntaxOff

\begin{document}

\myreplace{1-2-abc-345}

\myreplace[replace=--]{1-2-abc-345}

\myreplace[search=2,replace=0]{1-2-abc-345}

\end{document}

enter image description here


One way would be to use \StrSubstitute from the xstring package:

enter image description here

Code:

\documentclass{article}
\usepackage{xstring}

\newcommand{\myreplace}[2]{%
    \StrSubstitute{#2}{-}{.}[#1]%
}%

\begin{document}
\myreplace{\NewValue}{1-2-abc-345}
input: 1-2-abc-345 

output: \NewValue

\end{document}

Another solution using only TeX primitives:

\def\addto#1#2{\expandafter\def\expandafter#1\expandafter{#1#2}}
\def\replacestrings#1#2{%
   \def\tmp##1#1##2\end{\ifx\end##2\end\addto\tmpb{##1}\else\addto\tmpb{##1#2}\tmp##2\end\fi}%
   \expandafter\def\expandafter\tmpb\expandafter{\expandafter}\expandafter\tmp\tmpb#1\end 
}
\def\myreplace#1{\def\tmpb{#1}\replacestrings{-}{.}\tmpb}

\myreplace{1-2-abc-345}

Edit: I'll try to explain how it works. IMHO, it is more valuable to know how TeX works inside than only usage the code or package without thinking.

First line defines \addto<macro>{<text>} which adds to the <macro> the <text>. For example after \def\a{xx} \addto\a{yy} we have \a defined as xxyy.

The \replacestrings{<string1>}{<string2>} replaces all occurrences of <string1> in the \tmpb macro to the <string2>. First, the \tmp macro is defined at line 3 as:

\def\tmp#1<string1>#2\end{...}

Second, the \tmp is executed with expanded \tmpb as a parameter followed by \end. The \tmpb is (1) expanded, (2) it is set to empty and (3) the \tmp is executed. This is the reason of 5x \expandafter in line 4.

Suppose, we have \def\tmpb{abXXcdXXef} and we run \replacestring{XX}{YY}. Then \tmp is declared as the following pseudocode:

\def\tmp#1XX#2\end{if #2 is empty: \addto\tmpb{#1}
                   else: \addto\tmpb{#1YY}
                         \tmp #2\end
                   fi}

Then \tmp aaXXcdXXefXX\end is executed, but the \tmpb is set to empty first. The \tmp expands with #1=aa, #2=cdXXefXX. Because #2 isn't empty, the

\addto\tmpb{aaYY} \tmp cdXXefXX\end

is executed. So, \tmpb includes aaYY. The second run of \tmp expands with #1=cd #2=efXX to

\addto\tmpb{cdYY} \tmp efXX\end

Now, \tmpb includes aaYYcdYY. The third run of \tmp expands with #1=ef and #2 empty to

\addto\tmpb{ef}

This gives the result in \tmpb aaYYcdYYef.

An attentive reader can give notice that there is a bug: if the replaced text ends with one X, for example \def\tmpb{aaXXbbX} and <string1> is XX then there is a problem. It is true, but the question in this thread demands to replace only one token to one token. The real \replacestrings (used in OPmac, for example) does one more step: adds the special token at the end of the replaced text, then does the replacement and then removes this special token.

Tags:

Macros