# Process a string with replacing and splitting

I have an input string like "123-45" or "1-3-5" or "2-4; 6-8" where the minus signed should be removed. This is easy with the xstring package.

\StrSubstitute{#1}{-}{}


The next step is to split the input strings at the semicolon. This maybe could be done with \StrMid. The final output should be than:

123-45 = \somecommand{12345}
1-3-5 = \somecommand{135}
2-4 = \somecommand{24}
6-8 = \somecommand{68}


At the moment I have this not real working code:

\newcommand{\test}[1]{%
\newcommand{\stripped}{\StrSubstitute{#1}{-}{}}%
\IfInteger{\stripped}{\somecommand{\stripped}}{error}%
}


How can I solve this issue?

-
We are going to need some additional information. In your question text you talk about semicolons, yet I don't see any further reference to them. I would also like to know what doesn't work about your code. Does it give you an error? Is the result unexpected? In the latter case, what are you using as input, what is \sommecommand and what do you expect to get? – Roelof Spijker Dec 29 '11 at 20:27
\somecommand is in my case the \href command but this doesn't really matter in my opinion. The semicolons could be in the input string like in the third example "2-4; 6-8" – rekire Dec 29 '11 at 20:31
What do you mean by "split the input strings at the semicolon"? For example, xstring provides \StrSplit as well, that you're not using. – Werner Dec 29 '11 at 20:41
if i use my macro i get a error \test{1-23-45-67} throws this error: ! Argument of \reserved@a has an extra }. <inserted text> \par l.42 \test{1-23-45-67} – rekire Dec 29 '11 at 20:50

You can also use the xstring package:

\documentclass[a4paper, 11pt]{article}

\usepackage{xstring}
\def\somecommand#1{\textbf{#1}\par}
\makeatletter
\newcommand{\test}[1]{%
\StrSubstitute{#1}{-}{}[\stripped]%
\StrCount{\stripped}{;}[\@tempcnta]
\ifnum\@tempcnta=0
%no semicolon
\somecommand{\stripped}
\else
%semicolon
\@test{\stripped}%
\fi
}
\def\@test#1{%
\StrBefore{#1}{;}[\@tempa]
\StrBehind{#1}{;}[\@tempb]
\somecommand{\@tempa}
\test{\@tempb}
}
\begin{document}
\test{2-4; 6-8}

\test{123-45}
\end{document}

-

Without a "real use" case it's difficult to do more than providing a set of macros that split the argument at the semicolon and remove the hyphens.

Here's a way with xparse and LaTeX3; it assumes that at most one semicolon is present in the argument:

\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\somecommand}{>{\SplitArgument{1}{;}}m}
{ \somecommand_aux_i:nn #1 }
\cs_new:Nn \somecommand_aux_i:nn
{
\IfNoValueTF{#2}
{ \somecommand_aux_ii:nn {1}{#1} }
{ \somecommand_aux_ii:nn {1}{#1} \somecommand_aux_ii:nn {2}{#2} }
}
\cs_new:Nn \somecommand_aux_ii:nn
{
\tl_set:Nn \l_tmpa_tl {#2}
\tl_remove_all:Nn \l_tmpa_tl {-}
\tl_remove_all:Nn \l_tmpa_tl {~}
\somecommand_final:nx { #1 } { \l_tmpa_tl }
}
\cs_new:Nn \somecommand_final:nx
{ Integer~#1~is~#2 \par }
\ExplSyntaxOff


Now the input

\somecommand{1-2}

\somecommand{1-2; 3-5}

\somecommand{1-3-5}


will produce

It's just a matter of defining suitably \somecommand_final:nn

A different implementation using comma separated lists (and avoiding xparse), which assumes that no commas appear in the argument and allows for any number of semicolons:

\usepackage{expl3}
\ExplSyntaxOn
\clist_new:N \somecommand_clist
\cs_new:Npn \somecommand #1
{
\int_set:Nn \l_tmpa_int {0}
\tl_set:Nn \l_tmpa_tl {#1}
\tl_remove_all:Nn \l_tmpa_tl {-}
\tl_remove_all:Nn \l_tmpa_tl {~}
\tl_replace_all:Nnn \l_tmpa_tl {;} {,}
\clist_set:Nx \somecommand_clist { \l_tmpa_tl }
\clist_map_function:NN \somecommand_clist \somecommand_mapping:n
}
\cs_new:Nn \somecommand_mapping:n
{
\int_incr:N \l_tmpa_int
\somecommand_final:nn { \int_to_arabic:n {\l_tmpa_int } } { #1 }
}
\cs_new:Nn \somecommand_final:nn
{ Integer~#1~is~#2 \par}
\ExplSyntaxOff


Note that the \l_tmpa_int is just to provide indices to the various semicolon separated parts.

## Explanation

Let's look at the second implementation in order to understand the steps.

(1) We allocate a "clist" variable (comma separated list)

\clist_new:N \somecommand_clist


(2) We define the main command, with one argument.

\cs_new:Npn \somecommand #1
{
\int_set:Nn \l_tmpa_int {0}
\tl_set:Nn \l_tmpa_tl {#1}
\tl_remove_all:Nn \l_tmpa_tl {-}
\tl_remove_all:Nn \l_tmpa_tl {~}
\tl_replace_all:Nnn \l_tmpa_tl {;} {,}
\clist_set:Nx \somecommand_clist { \l_tmpa_tl }
\clist_map_function:NN \somecommand_clist \somecommand_mapping:n
}


It first of all initializes the scratch integer variable \l_tmpa_int to zero and sets the scratch token list variable \l_tmpa_tl to the argument. Then we delete from its contents all hyphens and all spaces (~ is LaTeX3 way of denoting a space when \ExplSyntaxOn is in force); finally we change all semicolons into commas and convert the token list into a clist (this is actually redundant, but for the sake of using the right data types we behave as good boys). Finally we do \clist_map_function:NN. If the clist is

{a,b,c}


this is equivalent to perform

\somecommand_mapping:n {a}
\somecommand_mapping:n {b}
\somecommand_mapping:n {c}


(3) We must provide a meaning for the mapping function:

\cs_new:Nn \somecommand_mapping:n
{
\int_incr:N \l_tmpa_int
\somecommand_final:nn { \int_to_arabic:n {\l_tmpa_int } } { #1 }
}


Just increment the scratch integer variable and perform \somecommand_final:nn with first argument the integer converted in printable form and the current clist item.

(4) Here is the final command which does something with the arguments

\cs_new:Nn \somecommand_final:nn
{ Integer~#1~is~#2 \par}


Of course one is free to do whatever is required.

## Another try (following Joseph Wright's suggestion)

Also this one works with any number of semicolons; we exploit the fact that \SplitList trims spaces on both sides; with an input such as \somecommand{1-2; 3-4} it will return #1 in the form {1-2}{3-4}:

\ExplSyntaxOn
\NewDocumentCommand{\somecommand}{>{\SplitList{;}}m}
{
\int_set:Nn \l_tmpa_int {0}
\tl_map_function:nN {#1} \somecommand_mapping:n
}
\cs_new:Nn \somecommand_mapping:n
{
\int_incr:N \l_tmpa_int
\tl_set:Nn \l_tmpa_tl {#1}
\tl_remove_all:Nn \l_tmpa_tl {-}
\somecommand_final:nn { \int_to_arabic:n {\l_tmpa_int} } { \l_tmpa_tl }
}
\cs_new:Nn \somecommand_final:nn
{ Integer~#1~is~#2 \par }
\ExplSyntaxOff

-
...you're really getting into this whole "LaTeX3 thing" quickly! – Werner Dec 29 '11 at 20:51
@Werner That's part of the plan: we provide a proper toolkit, and other people can use it without needing to re-invent the wheel yet again. – Joseph Wright Dec 29 '11 at 20:55
Uh that is hard to understand for me... – rekire Dec 29 '11 at 20:56
\documentclass[a4paper, 11pt]{article}
\usepackage{xstring}
\makeatletter
\def\test#1{\StrSubstitute{#1}{-}{}[\stripped]%
\expandafter\test@i\stripped;;\@nil}
\def\test@i#1;#2;#3\@nil{#1#2}
\makeatother

\begin{document}
\test{2-4;6-8}

\test{123-45}
\end{document}


if more than one semicolon are possible replace \text@i with

\def\test@i#1;#2;#3\@nil{#1#2%
\ifx\relax#3\relax\else\test@i#3\@nil\fi}

-

An old solution. This solution comes from TeX in Practice. I use this method when I need to replace some characters. This is a long code but the macro can be stocked in a file with others utilities or tools.

\documentclass[a4paper, 11pt]{article}
\makeatletter
%<–––––––– code from TeX in Practice ––––––––>
\newif\if@TestSubString
\def\SubStringConditional #1#2{%
TT\fi
\edef\@MainString{#1}%
\edef\@SubStringConditionalTemp{{#1}{#2}}%
\expandafter\@SubStringConditional\@SubStringConditionalTemp
}
\def\@SubStringConditional #1#2{%
\def\@TestSubS ##1#2##2\\{%
\def\@TestTemp{##1}%
}%
\@TestSubS #1#2\\
\ifx\@MainString\@TestTemp
\@TestSubStringfalse
\else
\@TestSubStringtrue
\fi
\if@TestSubString
}

\def\RecursionMacroEnd #1#2#3{%
#1\relax
\def\@RecursionMacroEndNext{#2}%
\else
\def\@RecursionMacroEndNext{#3}%
\fi
\@RecursionMacroEndNext
}

\def\ReplaceSubStrings #1#2#3#4{%
\def\@ReplaceResult{#1}%
\edef\@ReplaceMain{#2}%
\edef\@ReplaceSub{#3}%
\edef\@ReplaceSubRep{#4}%
\@ReplaceSubStrings
}
\def\@ReplaceSubStrings{%
\RecursionMacroEnd
{\if\SubStringConditional{\@ReplaceMain}{\@ReplaceSub}}%
{\@ReplaceSubStringsDo}{\expandafter\edef\@ReplaceResult{\@ReplaceMain}}%
}
\def\@ReplaceSubStringsDoX{%
\def\@ReplaceSubStringsDoA ##1%
}%
\def\@ReplaceSubStringsDo{%
\expandafter\@ReplaceSubStringsDoX \@ReplaceSub
##2\@EndReplaceSubStrings{%
\edef\@ReplaceMain{##1\@ReplaceSubRep ##2}%
}%
\expandafter\@ReplaceSubStringsDoA\@ReplaceMain
\@EndReplaceSubStrings
\@ReplaceSubStrings
}
\makeatother

\begin{document}
\def\awordwithminus{1-3------6-5}
\ReplaceSubStrings{\awordwithoutminus}{\awordwithminus}{-}{}
\awordwithoutminus
\end{document}

-
\documentclass{article}
\usepackage{catoptions}
\makeatletter
% #1 = token to parse
% #2 = string to remove
% #3 = replacement
% #4 = number of replacements
\new@def*\replacestrNtimes#1#2#3#4{%
\expandafter\cptswap\expandafter{\expandafter{\romannumeral\number#4000}}%
{\cpt@replacestrNtimes#1\@empty\@nnil{#2}{#3}}{}%
}
\new@def*\cpt@replacestrNtimes#1#2\@nnil#3#4#5#6{%
\ifnullTF{#5}{#6#1#2}{%
\ifstrcmpTF{#1}\@empty{#6}{%
\ifstrcmpTF{#1}{#3}{%
\expandafter\cptswap\expandafter{\expandafter{\@gobble#5}}%
{\cpt@replacestrNtimes#2\@nnil{#3}{#4}}{#6#4}%
}{%
\cpt@replacestrNtimes#2\@nnil{#3}{#4}{#5}{#6#1}%
}%
}%
}%
}
\makeatother

% Examples:
%\edef\x{\replacestrNtimes{x--y-z}{-}{+}{2}}
%\edef\x{\replacestrNtimes{x--y-z}{-}{+}{4}}
%\edef\x{\replacestrNtimes{x--y-z}{-}{+}{1}}
%\edef\x{\replacestrNtimes{x--y-z}{-}{+}{0}}
\edef\x{\replacestrNtimes{x--y-z}{-}{}{2}}
\show\x

-