2

I try to write some content in an external file using TeX' \immediate\write command.

\documentclass{scrartcl}

\begin{document}

\newwrite\tempfile
\immediate\openout\tempfile=lists.tex
\immediate\write\tempfile{line1}
\immediate\write\tempfile{}
\immediate\write\tempfile{line2}
\immediate\closeout\tempfile

\input{lists}

\end{document}

Is there a method to replace a line (e.g. "line2") in this lists.tex later in the code?

1
  • 3
    short answer, no. longer answer you could close the file then read it in line by line saving the lines then close it again then open it for writing then write out the data making the change. There is no point to doing this you could skip the first write and just write after the data is fixed. Commented Mar 9, 2020 at 22:15

2 Answers 2

3

TeX doesn't allow you to open files with append or overwrite permission: only read and write exist. You can fake that behavior by reading the file in, replacing what you want to replace, and then rewriting.

Here's an expl3 implementation of \replacelineonce and \replacelineall, which will replace one or all occurrences of a given string in the file. The syntax is:

\replacelineonce{<file>}{<search string>}{<replacement>}
  {<true code>}{<false code>}

The code will read <file> line by line as strings (characters like \ and { etc. don't have their special meaning), looking for the <search string> (only full lines are matched), and if found, will replace by <replacement>. After replacing, the file is rewritten with the changed contents. If any replacement was made, <true code> is executed at the end, otherwise <false code> is executed. If the input file didn't exist, an error is raised and <false code> is executed. \replacelineonce will stop after the first replacement, while \replacelineall will replace all ocurrences.

\documentclass{article}

\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand \replacelineonce { m m m m m }
  { \mountain_replace_once:nnnTF {#1} {#2} {#3} {#4} {#5} }
\NewDocumentCommand \replacelineall { m m m m m }
  { \mountain_replace_all:nnnTF {#1} {#2} {#3} {#4} {#5} }
\tl_new:N \l__mountain_tmpa_tl
\tl_new:N \l__mountain_file_seq
\bool_new:N \l__mountain_replaced_bool
\ior_new:N \l__mountain_replace_ior
\iow_new:N \l__mountain_replace_iow
\prg_new_protected_conditional:Npnn \mountain_replace_once:nnn #1 #2 #3 { T, F, TF }
  { \__mountain_replace_aux:Nnnn \c_false_bool {#1} {#2} {#3} }
\prg_new_protected_conditional:Npnn \mountain_replace_all:nnn #1 #2 #3 { T, F, TF }
  { \__mountain_replace_aux:Nnnn \c_true_bool {#1} {#2} {#3} }
\cs_new_protected:Npn \__mountain_replace_aux:Nnnn #1 #2 #3 #4
  {
    \ior_open:NnTF \l__mountain_replace_ior {#2}
      { \__mountain_replace_line:Nnnn #1 {#3} {#4} {#2} }
      {
        \msg_error:nnn { mountain } { file-not-found } {#2}
        \prg_return_false:
      }
  }
\cs_new_protected:Npn \__mountain_replace_line:Nnnn #1 #2 #3 #4
  {
    \seq_clear:N \l__mountain_file_seq
    \bool_set_false:N \l__mountain_replaced_bool
    \ior_str_map_inline:Nn \l__mountain_replace_ior
      {
        \str_if_eq:nnTF {##1} {#2}
          {
            \bool_set_true:N \l__mountain_replaced_bool
            \seq_put_right:Nn \l__mountain_file_seq {#3}
            \bool_if:NF #1
              { \ior_map_break:n { \__mountain_replace_skip: } }
          }
          { \seq_put_right:Nn \l__mountain_file_seq {##1} }
      }
    \__mountain_replace_end:n {#4}
  }
\cs_new_protected:Npn \__mountain_replace_skip:
  {
    \ior_str_map_inline:Nn \l__mountain_replace_ior
      { \seq_put_right:Nn \l__mountain_file_seq {##1} }
  }
\cs_new_protected:Npn \__mountain_replace_end:n #1
  {
    \ior_close:N \l__mountain_replace_ior
    \iow_open:Nn \l__mountain_replace_iow {#1}
    \seq_map_inline:Nn \l__mountain_file_seq
      { \iow_now:Nn \l__mountain_replace_iow {##1} }
    \iow_close:N \l__mountain_replace_iow
    \bool_if:NTF \l__mountain_replaced_bool
      { \prg_return_true: }
      { \prg_return_false: }
  }
\msg_new:nnn { mountain } { file-not-found }
  { File~`#1'~not~found. }
\ExplSyntaxOff

\begin{document}

\newwrite\tempfile
\immediate\openout\tempfile=lists.tex
\immediate\write\tempfile{line1}
\immediate\write\tempfile{}
\immediate\write\tempfile{line2}
\immediate\write\tempfile{}
\immediate\write\tempfile{line2}
\immediate\write\tempfile{}
\immediate\write\tempfile{line2}
\immediate\closeout\tempfile

\replacelineonce{lists.tex}{line2}{line replaced}
  {Replaced once:}
  {Nothing replaced:}

\input{lists}
\bigskip

\replacelineall{lists.tex}{line2}{line replaced}
  {Replaced all:}
  {Nothing replaced:}

\input{lists}
\bigskip

\replacelineonce{lists.tex}{line2}{line replaced}
  {Replaced once:}
  {Nothing replaced:}

\input{lists}
\bigskip

\end{document}
1

It's best to hold things in TeX if you need to make changes. this generates a list macro rather than writing the file, then it typesets the content, writes one file, then applies a change and typesets again, then writes a second file.

So the typeset output is

enter image description here

lists.tex gets written as

line1

line2

and lists2.tex gets written as

line1

line 2 changed

From the tex file:

\documentclass{scrartcl}

\newwrite\tempfile


\def\foo{}
\def\addline#1#2{\edef#1{\unexpanded\expandafter{#1\\{#2}}}}
\begin{document}



\addline\foo{line1}
\addline\foo{}
\addline\foo{line2}


{\def\\#1{#1 }\foo}% typeset it, was \input
{\def\\#1{#1^^J}
\immediate\openout\tempfile=lists.tex
\immediate\write\tempfile{\foo}
} % write first version
\immediate\closeout\tempfile



\def\\#1{%
\ifnum\pdfstrcmp{#1}{line2}=0
\unexpanded{\\{line 2 changed}}%
\else
\unexpanded{\\{#1}}%
\fi
}
\edef\foo{\foo} % apply the change

{\def\\#1{#1 }\foo}% typeset new version
{\def\\#1{#1^^J}
\immediate\openout\tempfile=lists2.tex
\immediate\write\tempfile{\foo}
} % write new version
\immediate\closeout\tempfile




\end{document}

You must log in to answer this question.

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