2

I'd like to output LaTeX code to a file, with the caveat that all backslashes are doubled (ie, \ is replaced with \\). This output will be consumed by another program.

I've adapted egreg's answer from Removing a backslash from a character sequence. It works when the resulting string is typeset, but I've been unable to write it to a file.

Here's a MWE with my attempts:

\documentclass{article}
\usepackage{xstring, etextools}
\begin{document}

%% Doubles all backslashes in the input argument
%% Adapted from: https://tex.stackexchange.com/a/42337/90126 (egreg)
\begingroup\lccode`\|=`\\
\lowercase{\endgroup\def\DoubleBackslashes#1{\StrSubstitute{#1}{|}{||}}}

\def\TestContents{Here are my contents: \UndefinedMacro and \\}

%% Prepare the output file
\newwrite\testfile
\immediate\openout\testfile=\jobname.TESTFILE

%% (1) This produces the expected output:
{\tt \DoubleBackslashes{\detokenize\expandafter{\TestContents}}}

%% (2) However, this doesn't:
%\immediate\write\testfile{\DoubleBackslashes{\detokenize\expandafter{\TestContents}}}

%% (3) We are able to write the un-doubled macro (as expected):
\immediate\write\testfile{\unexpanded\expandafter{\TestContents}}

%% (4) Using a intermediate macro doesn't work:
%\edef\TestContentsProtected{\expandnext\DoubleBackslashes{\detokenize\expandafter{\TestContents}}}
%\immediate\write\testfile{\unexpanded\expandafter{\TestContentsProtected}}

\immediate\closeout\testfile
\end{document}
  • What am I missing here?
  • How can this be fixed?
1
2

Since \StrSubstitute is not expandable, you need to do the substitution before writing:

\documentclass{article}
\usepackage{xstring}

\begingroup\lccode`\|=`\\
\lowercase{\endgroup\def\DoubleBackslashes#1{\StrSubstitute{\detokenize{#1}}{|}{||}}}

\newcommand{\doublebackslashwrite}[2]{%
  \DoubleBackslashes{#2}[\dbstemp]%
  \immediate\write#1{\dbstemp}%
}

%% Prepare the output file
\newwrite\testfile
\immediate\openout\testfile=\jobname.TESTFILE

\begin{document}

\texttt{\DoubleBackslashes{Here are my contents: \UndefinedMacro and \\}}

\doublebackslashwrite\testfile{Here are my contents: \UndefinedMacro and \\}

\immediate\closeout\testfile

\end{document}

Here's the contents of the written file:

Here are my contents: \\UndefinedMacro and \\\\

A different implementation that allows for several output streams and can distinguish between input given explicitly or via a macro.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\setupstream}{ O{default} m }
 {
  \iow_new:c { g_mblanc_dbswrite_#1_stream }
  \iow_open:cn { g_mblanc_dbswrite_#1_stream } { #2 }
  \AtEndDocument { \iow_close:c { g_mblanc_dbswrite_#1_stream } }
 }

\NewDocumentCommand{\dbswrite}{ s O{default} m }
 {
  \IfBooleanTF { #1 }
   {% argument is a macro
    \mblanc_dbswrite:nV { #2 } #3
   }
   {% argument is explicit
    \mblanc_dbswrite:nn { #2 } { #3 }
   }
 }

\tl_new:N \l__mblanc_dbswrite_text_tl

\cs_new_protected:Nn \mblanc_dbswrite:nn
 {
  \tl_set:Nx \l__mblanc_dbswrite_text_tl { \tl_to_str:n { #2 } }
  \tl_replace_all:Nxx \l__mblanc_dbswrite_text_tl
   { \c_backslash_str } { \c_backslash_str \c_backslash_str }
  \iow_now:cV { g_mblanc_dbswrite_#1_stream } \l__mblanc_dbswrite_text_tl
 }
\cs_generate_variant:Nn \mblanc_dbswrite:nn { nV }

\cs_generate_variant:Nn \tl_replace_all:Nnn { Nxx }
\cs_generate_variant:Nn \iow_now:Nn { cV }

\ExplSyntaxOff

\setupstream{\jobname.TESTFILE}
\setupstream[secondary]{\jobname.TESTFILESEC}

\newcommand{\test}{Here are my contents: \UndefinedMacro and \\}

\begin{document}

\dbswrite{Here are my contents: \UndefinedMacro and \\}
\dbswrite[secondary]{Here are my contents: \UndefinedMacro and \\}

\dbswrite*{\test}
\dbswrite*[secondary]{\test}

\end{document}
2

You can use following code. Backslash is searched and doubled in the loop. The \doublebb macro is fully expandable, so you can use it in \write arguments (output to a file) like \immediate\write\testfile{\doublebb\yourmacro}

\catcode`\/=0 \catcode`\\=12
/def/doublebb#1{/expandafter/doublebbA/detokenize/expandafter{#1}\/end\}
/def/doublebbA#1\{#1/doublebbB}
/def/doublebbB#1\{/ifx/end#1/empty/else \\#1/expandafter/doublebbB/fi}
/catcode`\=0 \catcode`\/=12

\def\test{Here are my contents: \UndefinedMacro and \others}
\message{\doublebb\test}

\bye

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.