Wenn man etwas in eine externe Datei schreiben möchte, kann man eine der folgender Methoden verwenden:

  • \write<handler>
  • \immediate\write<handler>
  • \protected@write<handler>

Was ist der Unterschied der verschiedenen Methoden und in was für Fällen bietet sich welche Methode an?

gefragt 06 Aug '14, 10:59

cgnieder's gravatar image

cgnieder
22.1k243463
Akzeptiert-Rate: 60%


\write schreibt sein sogenanntes balanced text-Argument expandiert in die über <handler> angegebene Datei, sobald die aktuelle Seite ausgegeben wird. (Die Ausgabe der Seite war ursprünglich gleichbedeutend mit dem Schreiben in die DVI-Datei). Der Vorteil dieses Vorgehens ist, dass beispielsweise \thepage bereits den Wert der aktuell ausgegeben Seite enthält.

\immediate\write wiederum schreibt das expandierte Argument sofort in die über <handler> angegebene Datei. LaTeX verwendet dies beispielsweise, um in die Haupt-aux-Datei bei \include Ladeanweisungen für die Unter-aux-Dateien zu schreiben.

Beispiel (\write):

Open in writeLaTeX
\documentclass{article}
\usepackage{blindtext}
\newcounter{test}
\makeatletter
\newcommand*{\dowrite}[1]{%
  \stepcounter{test}%
  \write\@mainaux{\string\def\string\testpage{\thetest: #1}}%
}
\makeatother
\begin{document}
\dowrite{\thepage}
\blindtext[1]
\dowrite{\thepage}
\blindtext[4]
\dowrite{\thepage}
\end{document}

schreibt in die aux-Datei:

\def\testpage{3: 1}
\def\testpage{3: 1}
\def\testpage{3: 2}
Die Erklärung dafür ist einfach: Der Zähler test wird bei jedem Aufruf von \dowrite um eins erhöht. Geschrieben wird aber tatsächlich erst am Ende des Dokuments, weil erst dann LaTeX erkennt, dass nicht mehr alles auf die erste Seite passt. Daher ist beim Schreiben der Zähler bereits test bereits 3. Der Zähler page wird erst beim Schreiben einer Seite erhöht. Daher ist er beim Schreiben der ersten Seite tatsächlich 1 und beim Schreiben der zweiten Seite tatsächlich 2.

Jetzt mit \immediate\write:

Open in writeLaTeX
\documentclass{article}
\usepackage{blindtext}
\newcounter{test}
\makeatletter
\newcommand*{\dowrite}[1]{%
  \stepcounter{test}%
  \immediate\write\@mainaux{\string\def\string\testpage{\thetest: #1}}%
}
\makeatother
\begin{document}
\dowrite{\thepage}
\blindtext[1]
\dowrite{\thepage}
\blindtext[4]
\dowrite{\thepage}
\end{document}

Hier lauten die Einträge in die aux-Datei:

\def\testpage{1: 1}
\def\testpage{2: 1}
\def\testpage{3: 1}
Nun haben wir quasi den umgekehrten Fall. Da die Einträge direkt bei der Ausführung von \dowrite geschrieben werden, befinden wir uns zu diesem Zeitpunkt noch immer auf Seite 1 und es wird der jeweils aktuelle Wert von test geschrieben. Die letzte \dowrite-Anweisung wird ebenfalls noch auf Seite 1 verarbeitet, weil erst beim impliziten Absatz von \end{document} erkannt wird, dass ein Teil des Materials der letzten \blindtext-Anweisung auf einer neuen Seite ausgegeben werden muss.

\protected@write verwendet intern ebenfalls \write. Allerdings unterscheidet es sich davon in zwei Punkten. Zunächst einmal definiert es \thepage so um, dass es bei der Expansion quasi zu sich selbst expandiert, und \protect so, dass es selbst erhalten bleibt und die Anweisung unmittelbar danach ebenfalls nicht expandiert wird. Dann führt es das nächste Argument aus und expandiert das übernächste mit \edef. Das so expandierte Argument wird dann wieder mit \write in die Datei geschrieben. In erster Linie bedeutet das, dass man bei Verwendung von \protected@write mit \protect arbeiten kann, um Expansionen zu verhindern, es ansonsten aber von \thepage abgesehen wie \immediate\write arbeitet:

Open in writeLaTeX
\documentclass{article}
\usepackage{blindtext}
\newcounter{test}
\makeatletter
\newcommand*{\dowrite}[1]{%
  \stepcounter{test}%
  \protected@write\@mainaux{}{\protect\def\protect\testpage{\thetest: #1}}%
}
\makeatother
\begin{document}
\dowrite{\thepage}
\blindtext[1]
\dowrite{\thepage}
\blindtext[4]
\dowrite{\thepage}
\end{document}

Hier erhalten wird:

\def \testpage {1: 1}
\def \testpage {2: 1}
\def \testpage {3: 2}
Das ist also eine Art Mischform aus \immediate\write und \write. Die unmittelbare Expansion wird durch das erwähnte \edef erreicht und führt dazu, dass der bei Ausführung von \dowrite aktualisierte Wert von test geschrieben wird. Die Expansion von \thepage wurde durch dessen Umdefinierung innerhalb von \protected@write verhindert, so dass der beim Schreiben der Seite gültige Wert von page geschrieben wird. Die zusätzlichen Lücken nach \def und \testpage kommen übrigens daher, dass hier kein String, sondern tatsächlich das nicht expandierte Makro geschrieben wird. Vergleichbar wäre das der Verwendung von \noexpand statt \string in den vorherigen Beispielen.

Stellt sich nun die Frage, wie wir auch \thetest erst beim Schreiben der Seite expandieren könnten. Dafür haben wir das im obigem Beispiel noch leere, also unbenutzte Argument:

Open in writeLaTeX
\documentclass{article}
\usepackage{blindtext}
\newcounter{test}
\makeatletter
\newcommand*{\dowrite}[1]{%
  \stepcounter{test}%
  \protected@write\@mainaux{\let\thetest\relax}{\protect\def\protect\testpage{\thetest: #1}}%
}
\makeatother
\begin{document}
\dowrite{\thepage}
\blindtext[1]
\dowrite{\thepage}
\blindtext[4]
\dowrite{\thepage}
\end{document}

Und schon erhalten wir wieder:

\def \testpage {3: 1}
\def \testpage {3: 1}
\def \testpage {3: 2}

Durch das \let\thetest\relax expandiert die Anweisung \thetest mit \edef quasi zu sich selbst und wird damit erst von \write beim Schreiben der Seite tatsächlich expandiert.

LaTeX selbst verwendet \protected@write beim Schreiben in die Hilfsdateien. Damit wird automatisch sichergestellt, dass \thepage das korrekte Ergebnis, also die Seitennummer der ausgegebenen Seite erhält und der Anwender zum Schutz zerbrechlicher Befehle in moving arguments \protect verwenden kann.

Das Paket scrlfile definiert außerdem noch eine \immediate-Variante von \protected@write. Diese definiert \thepage nicht um, da es ja ohnehin unmittelbar expandiert werden muss. Außerdem definiert es innerhalb von \BeforeClosingMainAux die Anweisung \protected@write so um, dass sie die \immediate-Variante verwendet. Das ist notwendig, damit Anweisungen wie \label, \addtocontents oder \addcontentsline, die innerhalb von \BeforeClosingMainAux verwendet werden, tatsächlich noch etwas in die aux-Datei schreiben, obwohl danach keine Seite mehr ausgegeben wird (bzw. laut Spezifikation ausgegeben werden darf).

Permanenter link

beantwortet 06 Aug '14, 11:57

gast3's gravatar image

gast3
(ausgesetzt)
Akzeptiert-Rate: 53%

bearbeitet 06 Aug '14, 12:12

Das ist tatsächlich erleuchtend, v.a. was \protected@write betrifft. Und ich sollte wohl mal die Anleitung zu scrlfile mal genau studieren. So einige Details scheinen da an mir vorbeigegangen zu sein.

(06 Aug '14, 20:30) cgnieder

Nachfolgefrage: ich nehme an, der Unterschied zwischen \edef und \protected@edef ist analog?

(08 Aug '14, 19:51) cgnieder

@Clemens: Nicht ganz. \protected@edef hat kein zusätzliches Argument und setzt auch nicht \thepage auf \relax. Ansonsten wird aber \protect genau wie bei \protected@write definiert. Bezüglich \protect verhält es sich also gleich. Und \protected@xdef ist dann das Analogon dazu bezüglich \xdef. \unrestored@protected@xdef definiert ebenfalls \protect um, restauriert aber im Gegensatz zu den vorgenannten nach der Makrodefinition \protect nicht wieder. Es sollte daher nur innerhalb von Gruppen verwendet werden. Ein \unrestored@protected@edef gibt es nicht.

(08 Aug '14, 20:31) gast3

@Ijon Danke. Der Punkt mit \thepage ergibt Sinn.

(08 Aug '14, 20:34) cgnieder
Deine Antwort
Vorschau umschalten

Folgen dieser Frage

Per E-Mail:

Wenn sie sich anmelden, kommen Sie für alle Updates hier in Frage

Per RSS:

Antworten

Antworten und Kommentare

Markdown-Grundlagen

  • *kursiv* oder _kursiv_
  • **Fett** oder __Fett__
  • Link:[Text](http://url.com/ "Titel")
  • Bild?![alt Text](/path/img.jpg "Titel")
  • nummerierte Liste: 1. Foo 2. Bar
  • zum Hinzufügen ein Zeilenumbruchs fügen Sie einfach zwei Leerzeichen an die Stelle an der die neue Linie sein soll.
  • grundlegende HTML-Tags werden ebenfalls unterstützt

Frage-Themen:

×20
×11

gestellte Frage: 06 Aug '14, 10:59

Frage wurde gesehen: 9,722 Mal

zuletzt geändert: 08 Aug '14, 20:34