5

I'd like to manipulate strings.

The following hello_en.txt should become hello.

This is related to, and quite possibly a duplicate of, Command doing the same job as StrBefore but being expandable. The problem there did not really solve mine, however.

Example

I tried putting an \expandafter before the first \StrBefore, but it did not work.

\documentclass{article}
\usepackage{fontspec}
\usepackage{xstring}
\newcommand\mystring{hello\_en.txt}
\begin{document}
\StrBefore{\StrBefore{\mystring{}}{.}}{\_} % more complex DOES NOT WORK
\StrBefore{\StrBefore{hello\_en.txt}{.}}{\_} % simpler DOES NOT WORK
\end{document}

3 Answers3

4

You must use something expandable, which \StrBefore isn't, and also be careful about the number of expansions.

\documentclass{article}

\usepackage{xstring}
\newcommand\mystring{hello\_en.txt}

\begin{document}

\expandarg
\StrBefore{\mystring}{.}[\temp]          
\StrBefore{\temp}{\noexpand\_}

\end{document}

The default of xstring is \fullexpandarg, which is to be neutralized as fully expanding \_ is not something one wants in these cases.

If you try \edef\tmp{\_} you get, as replacement text,

\protect\global\let\\textunderscore\unhbox\voidb@x\kern .06em\vbox{\hrule width.3em}\\textunderscore

or, if you have \usepackage[T1]{fontenc},

\protect\T1\textunderscore

so in both cases it's not what you want \StrBefore to look for.

egreg
  • 1,121,712
2

Looks like a job for l3regex to me

\documentclass{article}
\usepackage{l3regex}
\ExplSyntaxOn
\cs_new_eq:NN \regexreplace \regex_replace_all:nnN
\ExplSyntaxOff
\newcommand\mystring{hello\_en.txt}
\begin{document}
\regexreplace{\c{_}.*}{}\mystring
\show\mystring
\end{document}

(In the regex, \c{_} is a control sequence matching exactly \_ while .* is standard regex syntax for 'any character'.)

Joseph Wright
  • 259,911
  • 34
  • 706
  • 1,036
  • Thats funny because in the real document I am using ExplSyntax (LaTeX3). This question stems from iterating files: http://tex.stackexchange.com/questions/254247/how-can-i-iterate-over-a-paragraph-delimited-list-in-latex. I have a bunch of documents with ISO 639-1 language suffixes (en, de, etc.). They are contained in a master folder with the same name of the files but without the language suffix or extension. – Jonathan Komar Jul 08 '15 at 10:45
  • @macmadness86 If you have a clear pattern for the names then a classical delimited macro will be the most efficient approach. – Joseph Wright Jul 08 '15 at 10:49
  • Your answer is my favorite, but I must mark egreg's answer, because it fits the question more accurately. – Jonathan Komar Jul 09 '15 at 11:02
2

A solution with stringstrings.

\documentclass{article}
\usepackage{stringstrings}
\newcounter{tmpctr}
\newcommand\preunderscore[1]{%
  \whereischar[q]{#1}{_}%
  \setcounter{tmpctr}{\theresult}%
  \addtocounter{tmpctr}{-1}%
  \substring{#1}{1}{\thetmpctr}%
}
\begin{document} 
\def\mystring{hello_en.txt}
\preunderscore{\mystring} is the truncated text.
\end{document} 

If one wanted to specify the name with a quoted underscore, a small change is needed (which assumes there are no @ characters in the filename):

\documentclass{article}
\usepackage{stringstrings}
\newcounter{tmpctr}
\newcommand\preunderscore[1]{%
  \convertchar[q]{#1}{\_}{@}%
  \whereischar[q]{\thestring}{@}%
  \setcounter{tmpctr}{\theresult}%
  \substring{#1}{1}{\numexpr\thetmpctr-1}%
}
\begin{document} 
\def\mystring{hello\_en.txt}
\preunderscore{\mystring} is the truncated text.
\end{document}