Wie kann ich den Dateiinhalt erweitern, um Zeilen anzuhängen und zu zählen?

Wie kann ich den Dateiinhalt erweitern, um Zeilen anzuhängen und zu zählen?

Hier sind zwei Fragen zur filecontentsUmgebung:

  1. ist es möglich, einer bereits vorhandenen Datei Inhalt hinzuzufügen oder zumindest die externe Datei geöffnet zu halten, um den Inhalt mehrerer filecontentsUmgebungen an dieselbe externe Datei anzuhängen?

  2. ist es möglich herauszufinden, wie viele Zeilen von einer filecontentsUmgebung in die externe Datei geschrieben wurden?

Antwort1

Das Zählen der Zeilen filecontentsist einfach, da die Umgebung ihren Inhalt Zeile für Zeile verarbeitet, liest, prüft, ob die Umgebung beendet wurde, und schreibt dann. Sie müssen nur einen Zähler erstellen und ihn jedes Mal hinzufügen, wenn eine Zeile geschrieben wird.

Das Anhängen ist schwieriger. Erstens kann TeX (zumindest ohne Lua) keine Dateien mit der Berechtigung „Anhängen“ öffnen, also ist das ausgeschlossen. Die Idee, die Datei geöffnet zu lassen und später mit dem Schreiben fortzufahren, scheint zunächst einfacher, aber Sie müssten eine Reihe von Variablen in verschiedenen Umgebungen speichern, und dann könnten Sie nicht in die Datei schreiben a, dann bund dann an anhängen a.

Im folgenden Code liest das \filecontents@preappendMakro (wenn die appendOption verwendet wird) die Datei im Argument in ein temporäres Makro ein und \filecontents@appendfügt dann das gelesene Makro vor dem eigentlichen Inhalt der Umgebung in den Eingabestrom ein, sodass es genauso funktioniert, als ob Sie alles auf einmal eingegeben hätten. Die appendOption impliziert die Optionen overwriteund noheader.

Ich habe versucht, den Code der Kompaktheit halber als Patch zu schreiben, aber beim Patchen eines Befehls gehen anscheinend die ^^JZeichen verloren und der Header wird auf eine einzelne Zeile reduziert. Dies ist also eine vollständige Neudefinition von \filec@ntents, aber die geänderten Zeilen sind markiert.

\documentclass{article}
\usepackage{expl3}
\ExplSyntaxOn
\cs_new_eq:NN \FileSize \file_size:n
\ExplSyntaxOff
\makeatletter
\begingroup%
\@tempcnta=1
\loop
  \catcode\@tempcnta=12  %
  \advance\@tempcnta\@ne %
\ifnum\@tempcnta<32      %
\repeat                  %
\catcode`\^^M\active
\catcode`\^^L\active\let^^L\relax
\catcode`\^^I\active
\gdef\filec@ntents#1{%
  \set@curr@file{\filec@ntents@checkdir#1}%
  \edef\q@curr@file{\expandafter\quote@name\expandafter{\@curr@file}}%
  \filecontents@preappend% <- ADDED for appending
  \gaborit@reset@counter% <- ADDED for counting lines
  \gaborit@reset@append% <- ADDED for counting lines
  \openin\@inputcheck\q@curr@file \space %
  \ifeof\@inputcheck%
    \@latex@warning@no@line%
        {Writing file `\@currdir\@curr@file'}%
    \chardef\reserved@c15 %
    \ch@ck7\reserved@c\write%
    \immediate\openout\reserved@c\q@curr@file\relax%
  \else%
    \if@filesw%
      \@latex@warning@no@line%
          {File `\@curr@file' already \filec@ntents@where.\MessageBreak%
             Not generating it from this source}%
      \let\gaborit@step@counter\@empty% <- ADDED for counting lines
      \let\gaborit@count@header\@empty% <- ADDED for counting lines
      \let\write\@gobbletwo%
      \let\closeout\@gobble%
    \else%
      \edef\reserved@a{#1}%
      \edef\reserved@a{\detokenize\expandafter{\reserved@a}}%
      \edef\reserved@b{\detokenize\expandafter{\jobname}}%
      \ifx\reserved@a\reserved@b%
        \@fileswtrue%
      \else%
        \edef\reserved@b{\reserved@b\detokenize{.tex}}%
        \ifx\reserved@a\reserved@b
          \@fileswtrue%
        \fi%
      \fi%
      \chardef\reserved@c15 %
      \ch@ck7\reserved@c\write%
      \if@filesw%  % Foul ... trying to overwrite \jobname!
      \@latex@error{Trying to overwrite `\jobname.tex'}{You can't %
        write to the file you a reading from!\MessageBreak%
        Data is written to screen instead.}%
      \else%
        \@latex@warning@no@line%
           {Writing or overwriting file `\@currdir\@curr@file'}%
        \immediate\openout\reserved@c#1\relax%
      \fi%
    \fi%
  \fi%
  \closein\@inputcheck%
  \if@tempswa%
    \gaborit@count@header% <- ADDED for counting lines
    \immediate\write\reserved@c{%
      \@percentchar\@percentchar\space%
          \expandafter\@gobble\string\LaTeX2e file `\@curr@file'^^J%
      \@percentchar\@percentchar\space  generated by the %
        `\@currenvir' \expandafter\@gobblefour\string\newenvironment^^J%
      \@percentchar\@percentchar\space from source `\jobname' on %
         \number\year/\two@digits\month/\two@digits\day.^^J%
      \@percentchar\@percentchar}%
  \fi%
  \let\do\@makeother\dospecials%
  \count@ 128\relax%
  \loop%
    \catcode\count@ 11\relax%
    \advance\count@ \@ne%
    \ifnum\count@<\@cclvi%
  \repeat%
  \edef\E{\@backslashchar end\string{\@currenvir\string}}%
  \edef\reserved@b{%
    \def\noexpand\reserved@b%
         ####1\E####2\E####3\relax}%
  \reserved@b{%
    \ifx\gaborit@reset@append##1\relax% <- ADDED for counting lines
      \gaborit@reset@append% <- ADDED for counting lines
    \else% <- ADDED for counting lines
    \ifx\relax##3\relax%
      \immediate\write\reserved@c{##1}%
      \gaborit@step@counter% <- ADDED for counting lines
    \else%
      \edef^^M{\noexpand\end{\@currenvir}}%
      \ifx\relax##1\relax%
      \else%
          \@latex@warning{Writing text `##1' before %
             \string\end{\@currenvir}\MessageBreak as last line of \@curr@file}%
        \immediate\write\reserved@c{##1}%
        \gaborit@step@counter% <- ADDED for counting lines
      \fi%
      \ifx\relax##2\relax%
      \else%
         \@latex@warning{%
           Ignoring text `##2' after \string\end{\@currenvir}}%
      \fi%
    \fi%
    \fi% <- ADDED for counting lines
    ^^M}%
  \catcode`\^^L\active%
  \let\L\@undefined%
  \def^^L{\expandafter\ifx\csname L\endcsname\relax\fi ^^J^^J}%
  \catcode`\^^I\active%
  \let\I\@undefined%
  \def^^I{\expandafter\ifx\csname I\endcsname\relax\fi\space}%
  \catcode`\^^M\active%
  \edef^^M##1^^M{%
    \noexpand\reserved@b##1\E\E\relax}%
  \filecontents@append}% <- ADDED for appending
%
% Code for append
\gdef\gaborit@pre@append{%
  \begingroup%
    \catcode`\^^L\active%
    \catcode`\^^I\active%
    \catcode`\^^M\active%
    \let\do\@makeother\dospecials%
    \count@ 128\relax%
    \loop%
      \catcode\count@ 11\relax%
      \advance\count@ \@ne%
      \ifnum\count@<\@cclvi%
    \repeat%
    \let^^M\relax%
    \edef\gaborit@tmpa{\FileSize{\q@curr@file}}%
    \ifnum\expandafter\@car\gaborit@tmpa\@nil=0\relax%
      \endgroup \def\gaborit@append{^^M\gaborit@reset@append}%
    \else%
      \everyeof{\noexpand}%
      \edef\gaborit@tmpa{\@@input\q@curr@file \space}%
      \edef\x{\endgroup%
        \edef\noexpand\gaborit@append{%
          \noexpand\gaborit@trim@EOF\gaborit@tmpa\noexpand\gaborit@EOF}}\x%
    \fi%
  \filec@ntents@overwrite%
  \filec@ntents@noheader}%
\gdef\gaborit@trim@EOF#1^^M\gaborit@EOF{^^M#1%
  ^^M\noexpand\gaborit@reset@append}%
\endgroup%
\def\filec@ntents@append{%
  \let\filecontents@preappend\gaborit@pre@append
  \def\filecontents@append{\gaborit@append}}
\let\filecontents@preappend\@empty
\let\filecontents@append\@empty
%
% For counting lines
\newcounter{FC@total@lines}
\newcounter{FC@lines}
\def\gaborit@reset@counter{\setcounter{FC@total@lines}{0}}
\def\gaborit@reset@append{\setcounter{FC@lines}{0}}
\def\gaborit@step@counter{%
  \stepcounter{FC@total@lines}%
  \stepcounter{FC@lines}}
\def\gaborit@count@header{%
  \addtocounter{FC@total@lines}{4}%
  \addtocounter{FC@lines}{4}} % Number of lines in the header
\makeatother

\begin{filecontents}[overwrite]{testfile.tex}
a
b
c
\end{filecontents}
\typeout{\arabic{FC@total@lines} lines written; \arabic{FC@lines} appended}

\begin{filecontents}[append]{testfile.tex}
d
e
f
g
\end{filecontents}
\typeout{\arabic{FC@total@lines} lines written; \arabic{FC@lines} appended}

\begin{document}
\end{document}

Wenn dieser Code ausgeführt wird, druckt TeX Folgendes auf dem Terminal aus:

LaTeX Warning: Writing or overwriting file `./testfile.tex'.

7 lines written; 7 appended
(./testfile.tex)

LaTeX Warning: Writing or overwriting file `./testfile.tex'.

11 lines written; 4 appended

(der FC@total@linesenthält die Gesamtzahl der Zeilen in der Datei, und der FC@linesZähler enthält nur die Anzahl der angehängten Zeilen) und die Datei testfile.texenthält:

%% LaTeX2e file `testfile.tex'
%% generated by the `filecontents' environment
%% from source `test' on 2019/12/20.
%%
a
b
c
d
e
f
g


Wie Sie bemerkt haben, gilt in diesem Fall:

\begin{filecontents*}{testfile.tex}
\end{filecontents*}
%
\begin{filecontents}[append]{testfile.tex}
a
\end{filecontents}

(das heißt, schreiben Sie nichts und hängen Sie dann eine Zeile mit an a). Die Datei, die Sie erhalten, ist:


a

Das liegt daran, dass TeX beim Lesen einer leeren Datei diese so behandelt, als handele es sich um eine Datei mit einer einzigen leeren Zeile 1 . Daher lässt sich zwischen diesen beiden Fällen nicht leicht unterscheiden und man muss das kleinere Übel wählen: entweder beim Anhängen an eine leere Datei eine unechte leere Zeile am Anfang der Datei haben oder beim Schreiben in eine Datei, die eine einzige leere Zeile enthält, eine leere Zeile entfernen lassen.

Mit den in pdfTeX eingeführten Dateidienstprogrammen (die aber heutzutage auch in anderen Engines verfügbar sind) können Sie jedoch die Größe (in Bytes) einer Datei abfragen, sodass Sie für eine leere Datei ein anderes Verhalten erzielen können. Im Code habe ich expl3's verwendet \file_size:n, um nicht mit nicht vorhandenen Dateien umgehen zu müssen.


1. Sie können dies mit dieser Testdatei sehen:

\catcode`\@=11
\newwrite\test
\def\testit#1{\immediate\openout\test \jobname.testfile\relax #1%
  {\catcode`\^^M=13 \everyeof{\noexpand}%
   \edef\tmpa{\ifdefined\@@input \@@input \else \input \fi \jobname.testfile }\show\tmpa}%
  \immediate\closeout\test}%
\testit{}% empty file
\testit{\immediate\write\test{}}% single empty line
\csname stop\endcsname\bye

Dieses Verhalten liegt meiner Meinung nach daran, dass TeX \endlinecharam Ende jeder Zeile ein hinzufügt. Wenn es eine leere Datei liest, liest es trotzdem eine Zeile (auch wenn sie leer ist) und fügt dann das \endlinechar( ^^M) ein, wodurch es so aussieht, als hätte die Datei eine einzelne leere Zeile.

verwandte Informationen