# Replacing braces in strings

I know I should probably not want to, but say for the sake of discussion that I wanted to remove braces from a string for some purpose (background, now obsolete). From its package description, stringstrings should be up to the job.

I can't get it to work, though.

\documentclass{article}
\usepackage{stringstrings}

\begin{document}\centering\ttfamily
\def\teststring{{ab{cd}ef}}%
\detokenize\expandafter{\teststring} \\
\convertchar{\teststring}{\{}{} \\
\convertchar{\detokenize\expandafter{\teststring}}{\{}{}
\end{document}


Fun output in the first line if you remove \ttfamily, too.

Digging into the documentation of stringstrings some more, I found out that it inserts periods when it does not understand something. Indeed, braces are forbidden in the input:

Cannot use { and } in stringstrings arguments. However, use \LB ... \RB in lieu of { ... }; manipulate string in [e] mode & \retokenize.

How is that supposed to work? I tried this:

\convertchar[e]{\detokenize\expandafter{\teststring}}{\LB}{}%
\retokenize[v]{\thestring}


but that outputs the third line from above.

How can I replace braces in strings? Answers need not use stringstrings.

Not with stringstrings, but with either xstring or l3regex.

\documentclass{article}
\usepackage{xstring,xparse,l3regex}

% xstring
\newcommand{\removebraces}[2][\filename]{%
\StrRemoveBraces{#2}[#1]%
}

\edef\lbracetoken{\string{}
\edef\rbracetoken{\string}}
\newcommand{\dremovebraces}[2][\filename]{%
% we assume #2 is a detokenized string
\StrSubstitute{#2}{\lbracetoken}{}[#1]%
\StrSubstitute{#1}{\rbracetoken}{}[#1]%
}

% l3regex

\ExplSyntaxOn
\NewDocumentCommand{\xremovebraces}{sO{\filename}m}
{
\IfBooleanTF{#1}
{
\tl_set:NV #2 #3
}
{
\tl_set:Nn #2 { #3 }
}
\regex_replace_all:nnN { \{(.*?)\} } { \1 } #2
}
\ExplSyntaxOff

\begin{document}

xstring:

\removebraces{{file_name.test}.png}
\texttt{\detokenize\expandafter{\filename}}

\edef\test{\detokenize{{file_name.test}.png}}
\dremovebraces{\test}
\texttt{\filename}

\bigskip

l3regex:

\xremovebraces{{file_name.test}.png}
\texttt{\detokenize\expandafter{\filename}}

\edef\test{\detokenize{{file_name.test}.png}}
\xremovebraces*{\test}
\texttt{\filename}

\end{document}


In these macros I assume that there's only one level of inner braces. The macro \removebraces accepts any string, whereas \dremovebraces expects a detokenized one.

For \xremovebraces there's no expectation; the *-variant is used to give a macro as argument.

All four macros take an optional argument, whose default value is \filename, where the final result is stored.

• Why remove only matching pairs of braces? While this certainly has applications, it's not strictly required here, so I wonder what your thought process was. – Raphael May 28 '15 at 14:08

Here's a LuaLaTeX-based solution. It sets up two macros: \replacebraces to replace braces with dots, and \removebraces to remove them entirely.

Remark: If your test string contains characters that are "special" to TeX (other than curly braces, i.e., characters such as \$, &, and _), just encase the macros \removebraces and \replacebraces in a \detokenize\expandafter{...} "wrapper" statement in order to "print" the modified strings.

% !TEX TS-program = lualatex
\documentclass{article}

%% Lua-side code
\usepackage{luacode} % for "luacode" environment and "\luastring" macro
\begin{luacode}
function replace_braces ( s )
s = string.gsub (s, "[%{%}]", ".")
tex.sprint ( s )
end

function remove_braces ( s )
s = string.gsub (s, "[%{%}]", "")
tex.sprint ( s )
end
\end{luacode}

%% TeX-side code
\newcommand{\replacebraces}[1]{%
\directlua{ replace_braces ( \luastring{#1} )} }
\newcommand{\removebraces}[1]{%
\directlua{ remove_braces ( \luastring{#1} )} }

% Set up a "string" that contains curly braces
\def\teststring{uuu{ab{cd}ef}vvv}
\begin{document}

Test string, curly braces replaced with dots: \replacebraces{\teststring}

Test string, curly braces removed entirely: \removebraces{\teststring}
\end{document}

• That's one notch on my "why should I switch to LuaLaTeX?" stick. Thanks! – Raphael May 27 '15 at 17:24