6
\documentclass{article}
\usepackage{xstring}
\begin{document}
\StrBehind[2]{/foo/bar}{/}
\StrCount{/foo/bar}{/}
\StrBehind[\StrCount{/foo/bar}{/}]{/foo/bar}{/}
\end{document}

I'm trying to delete /foo/ out of /foo/bar using xstring. The first line in the document above does produce bar and the second line does produce 2, but the third line doesn't work. How do I make it work, or is there a simpler approach to do what I want?

6 Answers6

12

If I understand well what do you want: to expand the last segment separated by slashes from the parameter, i.e. /foo/bar should expand to bar and /foo/bar/next should expand to next. You can define following macro (using only TeX primitives):

\def\afterlastslash#1{\afterlsA#1/\end}
\def\afterlsA#1/#2{\ifx#2\end #1\else \expandafter\afterlsA\expandafter#2\fi}

\afterlastslash{/foo/bar} % expands to bar

\afterlastslash{/foo/bar/next} % expands to next

wipet
  • 74,238
7

I think a listofitems approach is simpler.

\documentclass{article}
\usepackage{listofitems}
\setsepchar[,]{/}%
\newcommand\postslash[1]{\readlist\myparse{#1}\myparse[-1]}
\begin{document}
\postslash{/foo/bar}

\postslash{bar}

\postslash{/foo/baz/bar} \end{document}

enter image description here

3

I tried doing the same thing with \expandafters, with no sucess. Besides, that much inline code is ugly.

\documentclass{article}
\usepackage{xstring}
\begin{document}
\StrCount{/foo/bar}{/}[\temp]
\StrBehind[\temp]{/foo/bar}{/}
\end{document}
John Kormylo
  • 79,712
  • 3
  • 50
  • 120
3

with lualatex

\documentclass{article}
\usepackage{luacode}
\begin{luacode}
function StrBehind(s0)
    local s1 = s0:split("/")
    tex.print(s1[#s1])
end
\end{luacode}
\newcommand\StrBehind[1]{\directlua{StrBehind("#1")}}
\begin{document}
\StrBehind{/foo/bar}

\StrBehind{/foo/bar/bza/foobar}

\StrBehind{foobar} \end{document}

user187802
  • 16,850
2

Here's an expl3 implementation, named \getbasename. We add /\q_nil/ at the end and test two items at a time (the first can be empty).

Similarly defined is \getdirname that returns the path.

\documentclass{article}

\ExplSyntaxOn \NewExpandableDocumentCommand{\getbasename}{m} { \michel_getbasename:w #1/\q_nil/ } \cs_new:Npn \michel_getbasename:w #1/#2/ { \quark_if_nil:nTF { #2 } {% reached the end, return the previous item #1 } {% go on: put back #2/ and test again \michel_getbasename:w #2/ } } \NewExpandableDocumentCommand{\getdirname}{m} { \michel_getdirname:w #1/\q_nil/ } \cs_new:Npn \michel_getdirname:w #1/#2/ { \quark_if_nil:nF { #2 } {% return #1/ and go on: put back #2/ and test again #1/ \michel_getdirname:w #2/ } } \ExplSyntaxOff

\begin{document}

\getbasename{/foo/bar}

\getbasename{foo/bar}

\getbasename{/foo//bar}

\getbasename{/foo/bar/next}

X\getbasename{}X

X\getbasename{/}X

X\getbasename{/foo/}X

\getdirname{/foo/bar}

\getdirname{foo/bar}

\getdirname{/foo//bar}

\getdirname{/foo/bar/next}

X\getdirname{}X

\getdirname{/}

\getdirname{/foo/}

\end{document}

The last three examples with \getbasename and the first one with \getdirname return nothing, as witnessed by “XX”.

enter image description here

The function \quark_if_nil:nTF returns true if its (braced) argument contains only \q_nil, false otherwise.

egreg
  • 1,121,712
2

Here is a another approach using underlying TeX capacity with delimited macros. Compared to answer by wipet it is not using conditional tests (which may fail fo various reasons such as braced material, I will give an example), but it also proceeds iteratively and expandably.

\documentclass{article}
\usepackage[T1]{fontenc}% for \texttt{->}

\makeatletter

\catcode`A 3 % create some improbable token as delimiter % as we are using LaTeX for the demo, perhaps % we should have used @nil

% switch utility \long\def\uppera@switch #1A#2#3\relax{#2}

% we incorporate slight overhead to expand in exactly two steps \long\def\afterlastslash#1{\romannumeral\afterlastslash@i \z@#1/A} \long\def\afterlastslash@i #1/#2{\uppera@switch #2{#1}A{\afterlastslash@i\z@#2}\relax}

\catcode`A 11 \makeatother

\begin{document}

\newcommand\test[1]{\texttt{\detokenize{\afterlastslash{#1}}} expands to \texttt{\afterlastslash{#1}}}

\test{/oof/bar} % expands to bar

\test{/{oof}/bar} % expands to bar

\test{{braced/path}/bar} % expands to bar

\test{{/path}/bar} % expands to bar

\test{/foo/bar/next} % expands to next

\newcommand\testedef[1]{\texttt{\detokenize{\edef\x{\afterlastslash{#1}}}} gives meaning \edef\x{\afterlastslash{#1}}\texttt{\meaning\x<after meaning>}}

\testedef{/foo/bar/next}

\testedef{/foo/bar/next/}

Check that two expansions suffice

\expandafter\expandafter\expandafter\def \expandafter\expandafter\expandafter\x \expandafter\expandafter\expandafter{\afterlastslash{/foo/bar/next}} \texttt{\meaning\x<after meaning>}

Show feature of brace removal of output % a limitation: there is brace removal of last part \edef\x{\afterlastslash{/foo/bar/{next}}}\texttt{\meaning\x<after meaning>}

Check nesting. We need te expand argument, but our macro was not constructed to expand it. So let's use \texttt{\string\expanded} and \texttt{\string\expandafter}. \begin{verbatim} \edef\x{\expandafter\afterlastslash\expandafter{% \expanded{\afterlastslash{/foo/bar/{/foo/bar}}}}} \end{verbatim} % \edef\x{\expandafter\afterlastslash\expandafter{\expanded{\afterlastslash{/foo/bar/{/foo/bar}}}}}\texttt{\meaning\x<after meaning>}

\end{document}

output of sample code 1

Variant which makes sure to protect output from brace removal:

\documentclass{article}
\usepackage[T1]{fontenc}% for \texttt{->}

\makeatletter

\catcode`A 3 % create some improbable token as delimiter % as we are using LaTeX for the demo, perhaps % we should have used @nil

% switch utility \long\def\uppera@switch #1A#2#3\uppera@switch{#2}

% we incorporate slight overhead to expand in exactly two steps % also we avoid brace removal of last item in this variant % (thanks to extending it by @nil and also by fetching two % items at a time)

\long\def\afterlastslash#1{\romannumeral\afterlastslash@i#1@nil/A/} \long\def\afterlastslash@i #1/#2/{% % notice that brace removal can happen in intermediate #2 but this % is not important, as the final "basename" is protected by the added @nil \uppera@switch #2{\afterlastslash@clean@empty#1}A{\afterlastslash@i#2/}\uppera@switch } % @empty token will prevent brace removal in this last step \long\def\afterlastslash@clean#1@nil{\expandafter\z@#1}

\catcode`A 11 \makeatother

% wipet https://tex.stackexchange.com/a/684771/293669 % \def\afterlastslash#1{\afterlsA#1/\end} % \def\afterlsA#1/#2{\ifx#2\end #1\else \expandafter\afterlsA\expandafter#2\fi}

\begin{document}

\newcommand\test[1]{\texttt{\detokenize{\afterlastslash{#1}}} expands to \texttt{\afterlastslash{#1}}}

\test{/oof/bar} % expands to bar

\test{/{oof}/bar} % expands to bar

\test{{braced/path}/bar} % expands to bar

\test{{/path}/bar} % expands to bar

\test{/foo/bar/next} % expands to next

\newcommand\testedef[1]{\texttt{\detokenize{\edef\x{\afterlastslash{#1}}}} gives meaning \edef\x{\afterlastslash{#1}}\texttt{\meaning\x<after meaning>}}

\testedef{/foo/bar/next}

\testedef{/foo/bar/next/}

Check that two expansions suffice

\expandafter\expandafter\expandafter\def \expandafter\expandafter\expandafter\x \expandafter\expandafter\expandafter{\afterlastslash{/foo/bar/next}} \texttt{\meaning\x<after meaning>}

Check no brace removal regarding last item \edef\x{\afterlastslash{/foo/bar/{last is kept braced}}}\texttt{\meaning\x<after meaning>}

\end{document}

output

enter image description here

user691586
  • 1,988