5

LaTeX contains an internal macro \filename@parse that decomposes a filename into path and basename. Applying it to a filename that contains unicode characters, in conjunction with pdflatex, does not work, however.

$ pdflatex test
...
> \filename@base=macro:
->\LGR\textalpha \LGR\texttau \LGR\textalpha .
l.6 \show\filename@base

where test.tex contains

\documentclass{article}
\usepackage[greek]{babel}
\begin{document}
\makeatletter
\filename@parse{ατα.tex}
\show\filename@base
\makeatother
\end{document}

\LGR\textalpha \LGR\texttau \LGR\textalpha apparently is not a proper filename.

Using xelatex or lualatex instead of pdflatex works, but requires to set up a Greek font separately. On the other hand, \input{ατα.tex} works also under pdflatex.

Is there an equivalent of \filename@parse that does not expand the Unicode characters? (Apparently babel defines them as macros.)

0

4 Answers 4

6

You can use \file_parse_full_name:nNNN (the expandable version, \file_parse_full_name:n, is used in the implementation of the current filename parsing in the kernel, used for example in \input). You can define a wrapper around \file_parse_full_name:nNNN, so that it works exactly like \filename@parse:

\documentclass{article}
\usepackage[greek]{babel}
\ExplSyntaxOn \makeatletter
\cs_new_protected:Npn \safe@filename@parse #1
  {
    \file_parse_full_name:nNNN {#1}
      \filename@area \filename@base \filename@ext
  }
\ExplSyntaxOff
\begin{document}
\makeatletter
\filename@parse{ατα.tex}
\show\filename@base
\safe@filename@parse{ατα.tex}
\show\filename@base
\makeatother
\end{document}

The terminal shows:

> \filename@base=macro:
->\LGR\textalpha \LGR\texttau \LGR\textalpha .
l.15 \show\filename@base

?
> \filename@base=macro:
->ατα.
l.17 \show\filename@base

?

Another option is to use \set@curr@file{<filename>} to normalise the file name to catcode 12 tokens, then you can safely use \filename@parse\@curr@file extract the parts you want:

\documentclass{article}
\usepackage[greek]{babel}
\begin{document}
\makeatletter
\set@curr@file{ατα.tex}
\filename@parse\@curr@file
\show\filename@base
\makeatother
\end{document}

Note that this approach will also follow file substitutions, if there are any set up for that filename.

5
  • Cool! Even filenames with hashes work out with \file_parse_full_name:nNNN while with \filename@parse you get errors about Illegal parameter numbers and the like. ;-) Jan 17 at 15:59
  • Thanks! This is probably the way to go, to get rid of any filename problems in the future and to be in line with \input. I probably should dig into the new LaTeX kernel at some occasion ...
    – gernot
    Jan 21 at 9:12
  • 1
    @gernot Yes, by using \file_parse_full_name:n will make the behaviour of your macro regarding filenames close to that of LaTeX. To make it even closer (allowing the full stack of file-related features) you could use \set@curr@file{<filename>}, then \filename@parse\@curr@file (which would also save you from underscores and colons ;-) Jan 21 at 11:05
  • 1
    @gernot By the way, one big difference between the answers here is that the \file_parse_full_name:n or \set@curr@file approaches expand macros in the file name, whereas the approach in other answers don't (which may or may not be what you want). The regex answer keeps macros untouched, so they may expand later (but they also may be hiding the extension). The other two turn the macro into a string of characters Jan 21 at 11:14
  • 1
    @PhelypeOleinik Thanks a lot, I appreciate your discussion of the fine points.
    – gernot
    Jan 21 at 12:30
5

I would simple detokenize the file name

\expandafter\filename@parse\expandafter{\detokenize{ατα.tex}}
1
  • Thanks! I like the simplicity and the absence of underscores and colons ;-)
    – gernot
    Jan 21 at 9:13
4

Just as an exercise a regex version. Of course it is better to use \file_parse_full_name.

\documentclass{article}
\usepackage[greek]{babel}
\begin{document}
\ExplSyntaxOn
% list (sequence) variable and string (token list) variable
\seq_new:N \l_path_seq
\tl_new:N \l_base_name
% split on / and store in list
\regex_split:nnNTF { / } { /home/user/ατα.ch.tex } \l_path_seq { } { }
% store last token from list in \l_base_name
\seq_pop_right:NN \l_path_seq \l_base_name
% remove .ext
\regex_replace_once:nnN { \.\w+\Z } { } \l_base_name
\show\l_base_name
\ExplSyntaxOff
\end{document}

Result:

> \l_base_name=macro:
->ατα.ch.
l.14 \show\l_base_name
3
  • Thanks for this creative solution, something to learn from! D.E.Knuth once characterized Unix as 20 different versions of regular expressions under one roof. Does LaTeX add another one, or is it essentially Posix, sed, awk, or whatever?
    – gernot
    Jan 21 at 9:25
  • 1
    @gernot It's not a brand new made up syntax, but it has some quirks due to the underlying TeX language (like the ominous presence of backslashes). The documentation says: "The syntax of regular expressions is mostly a subset of the pcre syntax (and very close to posix), with some additions due to the fact that TeX manipulates tokens rather than characters" Jan 21 at 10:56
  • 1
    @gernot instead of $ I used \Z here (which is not posix I think) mostly because $ was confusing my syntax highlighter :) but all the common syntax is supported. Section 8.1 and 8.2 in interface3.pdf have a very clear and complete list of the supported syntax.
    – Marijn
    Jan 21 at 12:24
2

I suggest grabbing filenames from the .tex-input as verbatim-arguments (v-type in xparse) and to apply \detokenize.

  • When grabbing as v-type-arguments you can have filenames with unbalanced curly braces and several consecutive spaces.
  • v-type ensures that no hashes/no explicit character tokens of category 6(parameter) and no control-sequence-tokens are tokenized. (\detokenize would double hashes and append spaces behind control-word-tokens and the result of detokenizing control-sequence-tokens depends on the current value of \escapechar.)
  • \detokenize in turn turns active characters into characters of category 12 and 10.
  • Characters of category-code 10 not belonging to the name of a control symbol regardless their code-point number in the TeX-engine's internal character representation scheme always being tokenized as explicit character tokens of category 10 and character code 32 must be kept in mind.
\makeatletter
\NewDocumentCommand\PassTofilename@parse{+v}{%
  \expandafter\filename@parse\expandafter{\detokenize{#1}}%
}
\PassTofilename@parse"\relax#}  {ατα.tex"
\show\filename@base
\stop

Console-output on a utf8-shell:

$ pdflatex test.tex
This is pdfTeX, Version 3.14159265-2.6-1.40.21 (TeX Live 2020) (preloaded format=pdflatex)
 restricted \write18 enabled.
entering extended mode
(./test.tex
LaTeX2e <2020-10-01> patch level 4
L3 programming layer <2021-02-18>
> \filename@base=macro:
->\relax#}  {ατα.
l.6 \show\filename@base
                       
? 
 )
No pages of output.
Transcript written on test.log.
1
  • Thanks. That's an elegant and clever solution, using the power of v arguments. Another advantage is that it still uses filename@parse, since my code tinkers with the code of filename@parse (to avoid splitting off the extension), which still works with this approach.
    – gernot
    Jan 21 at 9:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.