如何在执行 \write 之前对字符串进行替换?

如何在执行 \write 之前对字符串进行替换?

我想将 LaTeX 代码输出到一个文件,但要注意所有反斜杠都要加倍(即,\替换为\\)。此输出将被另一个程序使用。

我改编了 egreg 的回答从字符序列中删除反斜杠。当结果字符串被排版时它可以工作,但我无法将其写入文件。

以下是我尝试的 MWE:

\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}
  • 我在这里遗漏了什么?
  • 如何修复此问题?

答案1

由于\StrSubstitute不可扩展,因此在书写之前需要进行替换:

\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 are my contents: \\UndefinedMacro and \\\\

不同的实现允许多个输出流并可以区分明确给出的输入或通过宏给出的输入。

\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

您可以使用以下代码。在循环中搜索反斜杠并将其翻倍。该\doublebb宏完全可扩展,因此您可以在\write参数(输出到文件)中使用它,例如\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

相关内容