11

I would like to replace every backslash \ inside a macro (string) with a forwardslash /.

The macro is intended to contain a filename in Windows notation which should be replaced by a valid (La)TeX compatible filename. I know that it is preferable to process such strings before using them in a source file, but: is it possible to do it with (La)TeX?

My MWE is:

\documentclass{article}
\usepackage{etoolbox}

% The following macro is a placeholder.
% It should replace '\' by '/'
% #1: input
% #2: output
\newcommand{\replaceBS}[2]{%
  \edef\x{\noexpand\edef\noexpand#2{\noexpand\detokenize{\expandonce#1}}}%
  \x%
}

\begin{document}

\def\WindowsFilename{c:\files\examples\pictures\myfile.png}

\replaceBS{\WindowsFilename}{\TeXFilename}

Current result: \texttt{\TeXFilename}

Expected result: \texttt{c:/files/examples/pictures/myfile.png}

\end{document}

A solution for \replaceBS should be compilable with pdflatex.

I've tried things with the xstring package so far without success. Any solution with TeX, LaTeX3, special packages,... is welcome.

And just to be sure: Yes, I know how to do a replacement with an editor or an external tool ;-)

jub0bs
  • 58,916

2 Answers2

13
\documentclass{article}

\makeatletter
\newcommand{\replaceBS}[2]{{\escapechar=`/ 
  \xdef#2{\expandafter\zap@space\detokenize\expandafter{#1} \@empty}}}
\makeatother

\begin{document}

\def\WindowsFilename{c:\files\examples\pictures\myfile.png}

\replaceBS{\WindowsFilename}{\TeXFilename}

Current result: \texttt{\TeXFilename}

Expected result: \texttt{c:/files/examples/pictures/myfile.png}

\end{document}
David Carlisle
  • 757,742
  • Yes, that's it! I just tested successfully that the resulting \TeXFilename can be used to include images (or whatever). Thank you: that was fast :-) – Thomas F. Sturm Nov 24 '14 at 17:49
  • @ThomasF.Sturm I'm fairly sure that for that use you wouldn't have to change \ to / just detokenize it. – David Carlisle Nov 24 '14 at 17:51
  • Hmm... maybe, but that resulted in errors because detokenizing inserted spaces and the file could not be found. – Thomas F. Sturm Nov 24 '14 at 17:56
  • 1
    Spaces in the path will be zapped as well, but they shouldn't be used anyway. – egreg Nov 24 '14 at 18:38
  • @egreg yes there's a hint in the \zap@space command name:-) – David Carlisle Nov 24 '14 at 19:25
  • @DavidCarlisle Do you see a way to preserve existing spaces in the path like c:\my files\foo to c:/my files/foo? – Thomas F. Sturm Nov 26 '14 at 09:36
  • @ThomasF.Sturm they are not existing, if you use a command like \my then the space is never tokenized at all it is absorbed while making the \my token so \my1 and \my 1 are identical input. The only way to stop that is to change the catcode of \ before taking the string. If you do {\catcode'\Z=0 Zcatccode '\\=12 ZxdefZmystring{\c:\my a a \za}} then \mystring will have spaces and safe backslashes without any replacement needed. – David Carlisle Nov 26 '14 at 09:49
  • @DavidCarlisle I think I have it now :-) With your hint about the catcode \includegraphics did work, but \filename@parse still had some problems. Catcode change plus \replaceBS from @egreg's answer combined had no problems. Thank you again! – Thomas F. Sturm Nov 26 '14 at 17:23
  • @ThomasF.Sturm ah well "whoever" wrote \filename@parse decided that windows users would use / so it doesn't attempt to split on \ . I wonder who that was..... – David Carlisle Nov 26 '14 at 17:28
  • @DavidCarlisle :-) – Thomas F. Sturm Nov 26 '14 at 18:46
5

As far as I know, both MiKTeX and TeX Live for Windows accept paths where \ is replaced by /, so if you define your \WindowsFilename as

c:/files/examples/pictures/myfile.png

to begin with, you shouldn't have any problem.

However, I understand that the casual user might not know this and be used to DOS style paths.

If you don't plan to define the paths in the argument to other commands, you can use some expl3 features:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\setWindowsPath}{mv}
 {
  \cs_new:Npn #1 { #2 }
 }
\NewDocumentCommand{\replaceBS}{mm}
 {
  \tl_set_eq:NN #2 #1
  \tl_replace_all:NVn #2 \c_backslash_other_tl { / }
 }
\tl_const:Nx \c_backslash_other_tl { \cs_to_str:N \\ }
\cs_generate_variant:Nn \tl_replace_all:Nnn { NV }
\ExplSyntaxOff

\setWindowsPath{\WindowsFilename}{c:\files\examples\pictures\myfile.png}

\replaceBS{\WindowsFilename}{\TeXFilename}

\show\WindowsFilename
\show\TeXFilename

\setWindowsPath{\WindowsFilenameS}{c:\files\example spaced\pictures\myfile.png}

\replaceBS{\WindowsFilenameS}{\TeXFilenameS}

\show\WindowsFilenameS
\show\TeXFilenameS

Note that this preserves spaces in paths. Note also that the backslashes in the Windows filenames have been “neutralized”, because they have category code 12.

> \WindowsFilename=\long macro:
->c:\files\examples\pictures\myfile.png.
l.22 \show\WindowsFilename

? 
> \TeXFilename=macro:
->c:/files/examples/pictures/myfile.png.
l.23 \show\TeXFilename

? 
> \WindowsFilenameS=\long macro:
->c:\files\example spaced\pictures\myfile.png.
l.29 \show\WindowsFilenameS

? 
> \TeXFilenameS=macro:
->c:/files/example spaced/pictures/myfile.png.
l.30 \show\TeXFilenameS
egreg
  • 1,121,712
  • +1 Thank you for that answer which preserves spaces (and umlauts as I have tested). The intended use is for some Windows-generated data which may not be able to be filtered before input. Unfortunately, this would need the replacement to be inside another macros argument. Further, \WindowsFilename would have to be re-used many times which is not possible with the current code. – Thomas F. Sturm Nov 25 '14 at 12:10
  • I will investigate if I can do the replacement somehow automatically before LaTeX comes into play. – Thomas F. Sturm Nov 25 '14 at 12:12