1

I have a code function which needs to use temporary files for preserving part of the code which needs to be later used in the other code. I simplified the things to a minimum to represent the problem below. But command \immediate\write is not properly handling slashes in the parameter value. How to make it safe and treating everything as a string?

\documentclass{article}
\usepackage[utf8]{inputenc}

\immediate\newwrite\matchtwoleft
\immediate\openout\matchtwoleft=matchtwoleft.aux

\newcommand{\match}[1]{%
  \immediate\write\matchtwoleft{#1}%
}


\begin{document}

%this works
\match{an expos\string\'e}

%this doesn't
\match{an expos\'e}

\end{document}

The output of the file cat matchtwoleft.aux

an expos\'e
an expos\unhbox \voidb@x \bgroup \let \unhbox \voidb@x \setbox \@tempboxa \hbox {e\global \mathchardef \accent@spacefactor \spacefactor }\accent 19 e\egroup \spacefactor \accent@spacefactor

Any idea what to make the code of the \match command safe for slashes?

2
  • 2
    \immediate\write\matchtwoleft{\unexpanded{#1}} Commented May 5, 2020 at 14:09
  • 1
    @PhelypeOleinik This will not expand commands that probably need to be.
    – egreg
    Commented May 5, 2020 at 14:49

2 Answers 2

2

You want “protected write”. The kernel doesn't provide it, but it's easy to add it.

\documentclass{article}
\usepackage[utf8]{inputenc}

\newwrite\matchtwoleft
\immediate\openout\matchtwoleft=\jobname-write.dat

\makeatletter
\long\def\protected@iwrite#1#2#3{%
  \begingroup
  %\let\thepage\relax % useless here
  #2%
  \let\protect\@unexpandable@protect
  \edef\reserved@a{\immediate\write#1{#3}}%
  \reserved@a
  \endgroup
  \if@nobreak\ifvmode\nobreak\fi\fi
}
\newcommand{\match}[1]{%
  \protected@iwrite\matchtwoleft{}{#1}%
}
\makeatother


\begin{document}

%this works
\match{an expos\string\'e}

%this doesn't
\match{an expos\'e -- exposé -- gar\c con -- garçon}

\immediate\closeout\matchtwoleft

\end{document}

The written file will have

an expos\'e
an expos\'e -- exposé -- gar\c con -- garçon
3

Let me add a few remarks about creating and writing to external text files in LaTeX before providing some examples.

About

\immediate\newwrite\matchtwoleft

\write-handles for text-files are numbered from 0 to 15 in traditional TeX.

\newwrite is just a means for assigning a name (in terms of the name of a control-sequence) to a specific \write-handle, no matter if currently a text-file (via \openout or \immediate\openout) is associated to that \write-handle and opened for writing or not.

Thus you don't need \immediate with \newwrite. Basically \newwrite only (via \chardef) defines a control-sequence to yield a number which denotes the number of a \write-handle. The \chardef-operation for defining the control-sequence which denotes the number of the \write-handle will be performed immediately in terms of operations where \immediate has no effect.

\newwrite is just a means of assigning a name in terms of the name of a control-sequence to the number of a \write-handle and hereby ensuring that you don't try to use more \write-handles than available and that you don't erroneously address a \write-handle which is already in use for other purposes.


As you already showed in the code which accompanies your question, a name (in terms of the name of a control-sequence that gets defined via \chardef) can be assigned to a \write-handle via \newwrite.

Assigning a file to a \write-handle and creating that file anew (and hereby destroying a previous instance of it if existing) and opening it for writing is done via \openout.

Writing to a file is done via \write.

Closing a file and removing the association between \write-handle and file is done via \closeout (or via ending the (La)TeX-run).


There are four main issues in (La)TeX when it comes to writing to external text files:

Issue 1: Shall writing take place immediately when (La)TeX encounters the corresponding \write-directive or shall writing be delayed until the moment is reached when the page which (La)TeX is about to create is delivered to the output-file=.dvi-file or .pdf-file?

If you prefix \openout/\write/\closeout by \immediate, then the corresponding operation is not delayed until delivering the page which (La)TeX is about to create while encountering these directives to the output-file=.dvi-file or .pdf-file but the corresponding operation is carried out immediately.

Having means available for delaying/not delaying \write-operations etc is important: The reason is: Due to the asynchrony of the output-routine things like numbers of pages are reliably available only at the time of delivering the page to the output-file. The time of delivering the page to the output-file is the only moment where the number of the page on which a phrase of text ends up is reliably available. At earlier times you cannot reliably predict whether that phrase will end up on the page which LaTeX is about to create or whether it will end up on the page following that page. E.g., when writing information for referencing-labels to the .aux-files which also contain things for \pageref reliable prediction of page-numbers is important. Thus such things cannot be written immediately in terms of \immediate but should be written in delayed fashion, i.e., at the time of delivering the page in question to the output-file.

Issue 2: When shall expandable tokens be expanded? Shall they be expanded at all?

With \immediate\write expandable tokens are expanded immediately and writing takes place immediately instead of being delayed until delivery of the page to the output-file.

With delayed writing expansion of expandable tokens is also delayed until delivery of the page to the output-file. This implies a possible problem: Expandable tokens may be redefined in the time-span between encountering the \write statement and actually writing.
In LaTeX 2ε this problem usually is handled via \protected@write: \protected@write internally applies \edef for having things expanded that shall be expanded immediately and then applies (delayed) \write. \edef is applied in a way where LaTeX 2ε's \protect-mechanism is taken into account which allows prevention of expansion of tokens that shall not be expanded.

Issue 3: What about (La)TeX's peculiarities when it comes to writing files?

After reading/pre-processing/tokenizing the .tex-input-file everything is about so-called tokens in (La)TeX.

Thus things like \write are actually not applied to characters of the .tex-input-file but are applied to tokens that came into being during reading/pre-processing/tokenizing the .tex-input-file or due to expansion of expandable tokens.

Peculiarities are:

  • When (La)TeX is writing a control-word-token without expansion (be the control-word-token unexpandable, be expansion prevented by \noexpand or \protect, be the control-word-token part of the \the-expansion of the content of a token-register, or whatever), you will always get a character whose code-point-number in (La)TeX's internal character-encoding-scheme equals the value of the integer-parameter \escapechar. Usually the value of that parameter is 92, thus usually you get a backslash. Then you get a sequence of characters which denotes the name of that control-word-token. Then you get a space-character. You get that space-character even if you didn't type one behind the corresponding sequence in the .tex-input-file. In other words: Appending a space is part of the operation of unexpanded-writing a control-word-token.
  • With control-symbol-tokens things are different: Appending a space is not part of the operation of unexpanded-writing a control-symbol-token.
  • When (La)TeX is writing an explicit character-token of category code 6(parameter), you will get the corresponding character twice. (Usually the hash, #, is of category code 6 and therefore tokenizing hash-characters from the .tex-input-file usually yields such character-tokens—that's why this is called "hash-doubling".)
  • When (La)TeX during writing encounters a non-expandable explicit character-token whose code-point-number in (La)TeX's internal character-encoding-scheme equals the value of the integer-parameter \newlinechar, that character will not be written but will cause (La)TeX to start writing another line. The coding for starting another line in a text-file in turn is platform-dependent.
  • ^^-notation of characters might be transformed into normal notation and vice versa.

You can prevent (La)TeX from performing such peculiar things by having (La)TeX read from the .tex-input-file and tokenize things under verbatim-category-code-régime whereby everything gets tokenized as some sort of ordinary character-token.

Sometimes this is desired. Sometimes this is handy when it comes to writing hashes that shall not be doubled or to writing unbalanced curly braces or to writing %-characters and the like.

Issue 4: A file cannot be open both for writing and for reading (e.g., via \input) at the same time

This might be a problem: Assume you have directives for writing entries into an auxiliary file for an index or a glossary. On the one hand you wish to have the possibility of placing such directives throughout the entire source-code of your document. On the other hand you wish the information provided by that auxiliary file to be available during the LaTeX-run, even at the end because the index/glossary will be placed at the end of the document. This seems contradictive because as long as a file is opened for writing, the information provided by that file is not available for being read.

In LaTeX 2ε you can handle such problems by means of the \@starttoc-\addtocontents-mechanism.

If you say \@starttoc{foo}, then, if it exists, the file \jobname.foo will be input. Then a new \write-handle is allocated via \newwrite—that is: the number of the corresponding \write-handle will be available via the control-sequence \tf@foo which is defined via \chardef—, and via \immediate\openout that \write-handle gets assigned the file \jobname.foo whose previous instance (if existing) hereby gets destroyed and which is created anew and opened for writing.

If you say \addtocontents{foo}{bla bla}, then in delayed fashion but with immediately performed \edef-expansion under obeyance of LaTeX 2ε's \protect-mechanism a directive is written to the .aux-file:
\@writefile{foo}{bla bla}.
When the .aux-file is processed at the end of the LaTeX-run, these \@writefile-directives get carried out: In case the corresponding \write-handle has been allocated and opened for writing in the meantime, e.g., due to \@starttoc{foo}, bla bla will immediately be written (without expansion) to the file \jobname.foo.

Open \write-handles will be closed automatically by (La)TeX at the end of the (La)TeX-run.

This way you can provide directives for writing to an external file throughout the entire source-code of your LaTeX-document while nevertheless having that file available until (probably somewhere right before the end of that source-code) saying \@starttoc. (It is assumed that the file in question is not needed any more after processing the corresponding \@starttoc-directive.) Usually \@starttoc is "hidden" within the definitions of commands like \tableofcontents or \listoffigures or \listoftables.


In case you are interested in a way of doing things without the need of allocating many \write-handles simultaneously/in case you are interested in a way of writing more files than \write-handles are available, the scrwfile-package might be of interest to you.

With that package \@starttoc is modified not to allocate the \write-handle and open the corresponding file for writing immediately. (Opening the file immediately for writing is actually not necessary as in any case writing to that file will take place at the end of the LaTeX-run, when the .aux-file with its \@writefile-directives is processed.) Instead \write-handles for external files get allocated at the end of the LaTeX-run, at the time of processing the .aux-file and its \@writefile-directives:

Assume 49 external files shall be written by means of \addtocontents-→\@writefile-directives while only about 12 \write-handles are still available. The scrwfile-package will allocate \write-handles for the first 12 files and then process the \@ritefile-directives of the .aux-file. Then the scrwfile-package will re-allocate these \write-handles for writing to the next 12 files and then process the \@ritefile-directives of the .aux-file again. Etc, until all external files are written.


I strongly recommend not to use filename-extensions also used by LaTeX itself.
This reduces the risk of erroneously having files overwritten which one still needs.
Thus in the example below the external text file written during/at the end of the LaTeX-run will not be called matchtwoleft.aux but will be called matchtwoleft.mxt or ⟨jobname⟩.mxt (mxt being an abbreviation for my extension).


With the examples below, the verbatim-package is loaded. This is done only for having the \verbatiminput-command available.
The \verbatiminput-command in turn is only used for displaying the external text-file within LaTeX's output-file (.dvi-file or .pdf-file).


In case you need immediate-writing with some degree of expansion, but with expansion-prevention for things that turn out mean when expanded at times of writing to external files, I suggest this:

File text.tex:

\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage{verbatim}

\makeatletter
\newcommand{\match}[1]{%
  \@bsphack
  \begingroup
  \let\label\relax
  \let\index\relax
  \let\glossary\relax
  \let\protect\noexpand
  % \protect=\noexpand will ensure that commands defined in terms
  % of \DeclareRobustCommand or \newcommand with optional argument
  % or prefixed with \protect will not be expanded.
  \immediate\write\matchtwoleft{#1}%
  \endgroup
  \@esphack
}%
\makeatother

\begin{document}

\noindent
This is the content of the file \texttt{matchtwoleft.mxt}---spaces are displayed as ``\texttt{\char32}'':

\IfFileExists{matchtwoleft.mxt}{\verbatiminput*{matchtwoleft.mxt}}{}%

\newwrite\matchtwoleft
\immediate\openout\matchtwoleft=matchtwoleft.mxt
% Be aware that the file matchtwoleft.mxt is destroyed now and gets written anew.
% Thus it is not available throughout the entire LaTeX-run.

\match{an expos\'e
an expos\'e
an expos\'e}

\end{document}

enter image description here

In case you wish the possibility of giving directives for writing to the external file while not yet destroying its content, you can do something similar to the LaTeX 2ε-kernel's \@starttoc/\addtocontents-mechanism which writes @writefile{⟨write-handle⟩}{⟨stuff-to-write⟩}-directives to the .aux-file, which lead to writing ⟨stuff-to-write⟩ to the external file associated to ⟨write-handle⟩ at the end of the LaTeX-run.

In case you wish to have the .aux-file-route with immediate-writing with some degree of expansion, but with expansion-prevention for things that turn out mean when expanded at times of writing to external files, I suggest the following variant of \addtocontents:

File text.tex:

\makeatletter
\newcommand\myaddtocontents[2]{%
  \protected@write\@auxout{%
    \let\label\relax
    \let\index\relax
    \let\glossary\relax
    \begingroup
    \def\write{\noexpand\endgroup\let\noexpand\protect\noexpand\noexpand\immediate\noexpand\write}%
  }{\string\@writefile{#1}{#2}}%
}%
\makeatother

\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage{verbatim}
\makeatletter
%%================= Code for infrastructure for maintaining .mxt-file==========
\newcommand\creatematchfile{%
  \begingroup
  \let\saved@input=\@input
  \def\@input##1{\let\@input=\saved@input}%
  \@starttoc{mxt}%
  \endgroup
}%
\newcommand\printandcreatematchfile{%
  \IfFileExists{\jobname.mxt}{\verbatiminput*{\jobname.mxt}}{}%
  \creatematchfile
}%
\newcommand\match[1]{%
  \@bsphack
  \myaddtocontents{mxt}{#1}%
  \@esphack
}%
%%============== End of code for infrastructure for maintaining .mxt-file======
\makeatother

\begin{document}

\noindent
This is the content of the file \texttt{\jobname.mxt}---spaces are displayed as ``\texttt{\char32}'':

\printandcreatematchfile

\match{an expos\'e
an expos\'e
an expos\'e}

\end{document}

enter image description here

In case things shall be verbatim-copied from the .tex-input-file directly and written "verbatim", with no macro-expansion and the like taking place, you can have LaTeX process the things via one of xparse's verbatim arguments:

File text.tex:

\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage{xparse}
\usepackage{verbatim}

\makeatletter
\NewDocumentCommand{\match}{}{%
  \@bsphack
  \begingroup
  \newlinechar=\endlinechar
  \catcode\endlinechar=12 %
  \catcode`\^^I=12 %
  \matchinternal
}%
\NewDocumentCommand{\matchinternal}{+v}{%
  \immediate\write\matchtwoleft{#1}%
  \endgroup
  \@esphack
}%
\makeatother

\begin{document}

\noindent
This is the content of the file \texttt{matchtwoleft.mxt}---spaces are displayed as ``\texttt{\char32}'':

\IfFileExists{matchtwoleft.mxt}{\verbatiminput*{matchtwoleft.mxt}}{}%

\newwrite\matchtwoleft
\immediate\openout\matchtwoleft=matchtwoleft.mxt

\match{an expos\'e
an expos\'e
an expos\'e}

\end{document}

enter image description here

Besides this I can offer a variant of LaTeX 2ε's \@starttoc/\addtocontents-mechanism which does also read arguments under verbatim-catcode-régime.
Here I don't use xparse but my own things because xparse does not let you pass on the verbatim-delimiter, but the \@starttoc/\addtocontents-mechanism is a two-stage-process (stage 1: writing \@writefile-directives to .aux-file while processing the .tex-file, stage 2: writing to external file while processing the .aux-file and its \@writefile-directives) where the verbatim-delimiter is needed in the second stage also:

File text.tex:

\makeatletter
%%======================Code for \UDcollectverbarg=============================
%% \UDcollectverbarg{^^M-replacement}{<mandatory 1>}{<mandatory 2>}|<verbatim arg>|
%% 
%% reads <verbatim arg> under verbatim-catcode-regime and delivers:
%%
%%    <mandatory 1>{<mandatory 2>{<verbatim arg>}{|<verbatim arg>|}}
%%
%% Instead of verbatim-delimiter | the <verbatim arg> can be nested in braces.
%% You cannot use percent or spaces or horizontal tab as verbatim-delimiter.
%%
%% You can use <mandatory 1> for nesting calls to \UDcollectverbarg.
%% <mandatory 2> gets the <verbatim arg> twice: Once without verbatim-delimiters/braces,
%% once surrounded by verbatim-delimiters/braces.
%% Reason: When you feed it to \scantokens you don't need the verbatim-delimiters.
%%         When you use it for writing to temporary files and reading back,
%%         you need them.
%%=============================================================================
%% Check whether argument is empty:
%%=============================================================================
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%%    
%% Due to \romannumeral0-expansion the result is delivered after two
%% expansion-steps/after two "hits" by \expandafter.
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
%%
\long\def\UD@CheckWhetherNull#1{%
  \romannumeral0\expandafter\@secondoftwo\string{\expandafter
  \@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
  \@secondoftwo\string}\@firstoftwo\expandafter{} \@secondoftwo}%
  {\@firstoftwo\expandafter{} \@firstoftwo}%
}%
%%=============================================================================
\begingroup
\@makeother\^^M%
\@firstofone{%
  \endgroup%
  \newcommand\UDEndlreplace[2]{\romannumeral0\@UDEndlreplace{#2}#1^^M\relax{}}%
  \@ifdefinable\@UDEndlreplace{%
    \long\def\@UDEndlreplace#1#2^^M#3\relax#4#5{%
      \UD@CheckWhetherNull{#3}%
      { #5{#4#2}}{\@UDEndlreplace{#1}#3\relax{#4#2#1}{#5}}%
    }%
  }%
}%
\newcommand\UDcollectverbarg[3]{%
  \begingroup
  \let\do\@makeother % <- this and the next line switch to
  \dospecials        %    verbatim-category-code-régime.
  \catcode`\{=1      % <- give opening curly brace the usual catcode so a 
                     %    curly-brace-balanced argument can be gathered in
                     %    case of the first thing of the verbatimized-argument 
                     %    being a curly opening brace.
  \catcode`\ =10     % <- give space and horizontal tab the usual catcode so \UD@collectverbarg
  \catcode`\^^I=10   %    cannot catch a space or a horizontal tab as its 4th undelimited argument.
                     %    (Its 4th undelimited argument denotes the verbatim-
                     %     syntax-delimiter in case of not gathering a
                     %     curly-brace-nested argument.)
  \catcode`\%=14     % <- make percent comment.
  \kernel@ifnextchar\bgroup
  {% seems a curly-brace-nested argument is to be caught:
    \catcode`\}=2    % <- give closing curly brace the usual catcode also.
    \UD@collectverbarg{#1}{#2}{#3}{}%
  }{% seems an argument with verbatim-syntax-delimiter is to be caught:
    \do\{% <- give opening curly brace the verbatim-catcode again.
    \UD@collectverbarg{#1}{#2}{#3}%
  }%
}%
\newcommand\UD@collectverbarg[4]{%
  \do\ %   <- Now that \UD@collectverbarg has the delimiter or
  \do\^^I%    emptiness in its 4th arg, give space and horizontal tab
         %    the verbatim-catcode again.
  \do\^^M% <- Give the carriage-return-character the verbatim-catcode.
  \do\%%   <- Give the percent-character the verbatim-catcode.
  \long\def\@tempb##1#4{%
    \def\@tempb{##1}%
    \UD@CheckWhetherNull{#4}{%
      \def\@tempc{{##1}}%
    }{%
      \def\@tempc{#4##1#4}%
    }%
    \@onelevel@sanitize\@tempb % <- Turn characters into their "12/other"-pendants.
                               %    This may be important with things like the 
                               %    inputenc-package which may make characters 
                               %    active/which give them catcode 13(active).
    \expandafter\UDEndlreplace\expandafter{\@tempb}{#1}{\def\@tempb}% <- this starts 
                               %    the loop for replacing endline-characters.
    \@onelevel@sanitize\@tempc
    \expandafter\UDEndlreplace\expandafter{\@tempc}{#1}{\def\@tempc}%
    \expandafter\expandafter\expandafter\UD@@collectverbarg% <- this "spits out the result.
    \expandafter\expandafter\expandafter{%
    \expandafter\@tempb\expandafter}%
    \expandafter{\@tempc}{#2}{#3}%
  }%
  \@tempb
}%
\newcommand\UD@@collectverbarg[4]{%
  \endgroup
  #3{#4{#1}{#2}}%
}%
%%================= End of code for \UDcollectverbarg =========================
%%
%%================= Code for writing verbatim-args via aux-file================
\newcommand\UD@verbargs@addtocontents[2]{%
  % #1 - \@starttoc-\write-handle
  % #2 - Things to do after writing to .aux, e.g., \@esphack.
  \UDcollectverbarg{^^J}{\@firstofone}{\UD@@verbargs@addtocontents{#1}{#2}}%
}%
\newcommand\UD@@verbargs@addtocontents[4]{%
  \immediate\write\@auxout{\string\UD@verbarg@writefile{#1}#4}%
  #2%
}%
\newcommand\UD@verbarg@writefile[1]{%
 \UDcollectverbarg{^^J}{\@firstofone}{\UD@verbarg@@writefile{#1}}%
}%
\newcommand\UD@verbarg@@writefile[3]{%
  \@writefile{#1}{#2}%
}%
%%================= End of code for writing verbatim-args via aux-file=========
%%
\makeatother

\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage{verbatim}
\makeatletter
%%================= Code for infrastructure for maintaining .mxt-file==========
\newcommand\creatematchfile{%
  \begingroup
  \let\saved@input=\@input
  \def\@input##1{\let\@input=\saved@input}%
  \@starttoc{mxt}%
  \endgroup
}%
\newcommand\printandcreatematchfile{%
  \IfFileExists{\jobname.mxt}{\verbatiminput*{\jobname.mxt}}{}%
  \creatematchfile
}%
\newcommand\match{%
  \@bsphack
  \UD@verbargs@addtocontents{mxt}{\@esphack}%
}%
%%============== End of code for infrastructure for maintaining .mxt-file======
\makeatother

\begin{document}

\noindent
This is the content of the file \texttt{\jobname.mxt}---spaces are displayed as ``\texttt{\char32}'':

\printandcreatematchfile

\match{an expos\'e
an expos\'e
an expos\'e}

\match|annother expos\'e
another expos\'e
another expos\'e|

\end{document}

enter image description here

1
  • It is always a pleasure to read and learn from your explanations :) Commented Sep 15, 2020 at 16:52

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .