Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

How to define a command which replaces every certain character in its argument with another certain character?

For example, I'd like to define the command \myreplace{1-2-abc-345} which gives me the output 1.2.abc.345 by replacing every - with ..

share|improve this question
2  
If just normal text is involved in the argument (as opposed to macros), the stringstrings package might be of help. There are other such packages, too. I think regex is one, though I can't seem to locate it at the moment. –  Steven B. Segletes Jun 19 at 18:58
1  
The xstring paskage has a \StringSubstitute command? But is it tobe used with chains of characters? –  Bernard Jun 19 at 19:01

4 Answers 4

up vote 9 down vote accepted

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

share|improve this answer

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}
share|improve this answer

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.

share|improve this answer

Another solution using the stringstrings package as suggested by Steven B. Segletes:

\documentclass{article}
\usepackage{stringstrings}

\newcommand{\myreplace}[1]{\convertchar{#1}{-}{.}}

\begin{document}

\myreplace{1-2-abc-345} %output: 1.2.abc.345

\end{document}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.