4

This code crashes because I'm including the macro tmp into the label command. Reading this forum, especially What is the difference between Fragile and Robust commands? , I thought that the \protect I put in front of \tmp should fix the problem, but it doesn't. Could somebody please explain how to get it to work? Thanks very much for any suggestions

\documentclass{amsart}
\usepackage{soul}
\usepackage{xstring}
\begin{document}
\def\tmp{fig:\StrBefore{file.png}{.}}
\tmp
\begin{figure}
\caption{nothing}
\label{\protect\tmp}
%\label{\tmp}
\end{figure}
\end{document}
Leo Simon
  • 2,199
  • When it comes to processing file-paths and/or filenames it is always good to know whether folder-names/directory-names/filenames/filename-extensions with unbalanced curly braces and/or hashes can play a rôle. E.g., something like filen#{.ex} is a valid filename with filename-base filen#{ and filename-extension ex} in some file-systems. ... It is also good to know whether expansion of macros shall take place when obtaining tokens that form file-paths/filenames. – Ulrich Diez Apr 04 '20 at 20:19

3 Answers3

5

The macros of xstring are not, generally, expandable, which is needed here. Thus, I change over to listofitems, which produces expandable results.

\documentclass{amsart}
\usepackage{soul}
\usepackage{listofitems}
\begin{document}
\setsepchar{.}
\readlist\fileroot{file.png}
\edef\tmp{fig:\fileroot[1]}
\tmp
\begin{figure}
\caption{nothing}
\label{\tmp}
\end{figure}

I refer to figure \ref{fig:file}
\end{document}

enter image description here

And here's a version that doesn't even need listofitems:

\documentclass{amsart}
\usepackage{soul}
\newcommand\fileroot[1]{\filerootaux#1.\relax}
\def\filerootaux#1.#2\relax{#1}
\begin{document}
\edef\tmp{fig:\fileroot{file.png}}
\tmp
\begin{figure}
\caption{nothing}
\label{\tmp}
\end{figure}

I refer to figure \ref{fig:file}
\end{document}
4

I would just use the ability of xstring to store the result of the extraction in a macro.

\documentclass{amsart}
\usepackage{soul}
\usepackage{xstring}
\begin{document}
\StrBefore{file.png}{.}[\tmp]
\edef\tmp{fig:\tmp}
\tmp
\begin{figure}
\caption{nothing}
\label{\tmp}
\end{figure}
\end{document}

enter image description here

  • All three suggestions work, I accepted this one just because I like the xstring package and all it does, i.e., hysteresis. Thanks to all – Leo Simon Apr 05 '20 at 19:29
2

The argument to \label should be something that eventually becomes a string of characters, but \StrBefore eventually becomes instructions to print some characters, which is a very different thing.

It's simpler, unless your file names can have multiple periods in their names.

\documentclass{article}

\makeatletter
\newcommand{\stripext}[1]{\strip@ext#1.\@nil}
\def\strip@ext#1.#2\@nil{#1}
\makeatother

\begin{document}

\stripext{file.png}
\label{\stripext{file.png}}

\stripext{filenoext}
\label{\stripext{filenoext}}

\end{document}

The trick is to use a macro with delimited arguments. If we do \stripext{file.png}, we get

\strip@ext file.png.\@nil

and the macro \strip@ext uses as #1 whatever comes along until the first .; in this case it is file.

In the case of \stripext{filenoext}, we get

\strip@ext filenoext.\@nil

and things are good as well. In the first case #2 is png., in the second case it is empty; but the second argument is simply thrown away.

The .aux file from the code above will contain

\relax
\newlabel{file}{{}{1}}
\newlabel{filenoext}{{}{1}}

so you see it's as expected.

egreg
  • 1,121,712