Discussion:
Umlaute mit LaTeX in Datei schreiben und lesen
(zu alt für eine Antwort)
Ekkart Kleinod
2012-03-30 00:13:21 UTC
Permalink
Hi alle,

ich habe folgendes Problem: ich schreibe während des LaTeX-Laufs
Informationen in eine Datei (konkret: die Liste der Änderungen) und will
sie wieder einlesen.

Das Funktioniert gut, bis Umlaute ins Spiel kommen, die werden zwar
rausgeschrieben, aber dort expandiert(?) und nicht korrekt eingelesen.

Da das Inhaltsverzeichnis mit Umlauten funktioniert, vermute ich, dass
es eine Lösung gibt, ich habe den Code nur noch nicht gefunden.

Kann mir jemand helfen bzw. mich auf die richtige Stelle im Code stoßen?

Danke, Ekkart.

Minimalbeispiel: erste Ausgabezeile funktioniert beim Einlesen, die
zweite mit den Umlauten nicht. Nach einer Änderung bitte die temporäre
Datei test.txt löschen, damit sie neu angelegt wird.

minimal.tex

\documentclass[11pt, a4paper, ngerman]{article}

\usepackage{babel}
\usepackage[utf8]{inputenc}

\def\testchopline#1;#2 \\{
\def\first{#1}
\def\second{#2}
}

\begin{document}

\IfFileExists{test.txt}{
% lesen, ausgeben
\newread\infile
\openin\infile = test.txt
\read\infile to \testline
\expandafter\testchopline\testline\\
\closein\infile

Erster Teil: \first\\
Zweiter Teil: \second
}{
% schreiben
\newwrite\outfile
\immediate\openout\outfile = test.txt
\immediate\write\outfile{Test1;Test2} % funktioniert
%\immediate\write\outfile{Täst1; Tüst2} % funktioniert nicht, wg. Umlauten
\closeout\outfile
}

\end{document}
Ulrike Fischer
2012-03-30 08:21:49 UTC
Permalink
Post by Ekkart Kleinod
Hi alle,
ich habe folgendes Problem: ich schreibe während des LaTeX-Laufs
Informationen in eine Datei (konkret: die Liste der Änderungen) und will
sie wieder einlesen.
Das Funktioniert gut, bis Umlaute ins Spiel kommen, die werden zwar
rausgeschrieben, aber dort expandiert(?) und nicht korrekt eingelesen.
Da das Inhaltsverzeichnis mit Umlauten funktioniert, vermute ich, dass
es eine Lösung gibt, ich habe den Code nur noch nicht gefunden.
Du brauchst das Äquivalent zu \***@write. Das ist aber nicht
\immediate, also musst du den entsprechenden Code kopieren:

\newwrite\outfile
\immediate\openout\outfile = testblub.txt
\let\protect\@***@protect
\edef\***@a{\immediate\write\outfile{Tüte Täte}}%
\***@a
\immediate\closeout\outfile


Achte übrigens darauf, dass du maximal ein \newwrite-Befehl
ausführst. Die Ressourcen für write sind sehr begrenzt.
--
Ulrike Fischer
Ekkart Kleinod
2012-04-03 17:31:56 UTC
Permalink
ok, werde ich ausprobieren.

Vielen Dank für die Antwort, tut mir leid, dass ich erst jetzt antworte,
viel zu tun...

Gruß, Ekkart.
Ekkart Kleinod
2012-04-09 17:58:08 UTC
Permalink
Post by Ekkart Kleinod
\newwrite\outfile
\immediate\openout\outfile = testblub.txt
\immediate\closeout\outfile
Wenn ich das kopiere, funktioniert das gut.

In meiner Klasse habe ich (aus Versehen) das edef weggelassen, im
Beispiel also direkt

\immediate\write\outfile{Tüte Täte}

verwendet. Das hat auch funktioniert. Meine Frage: kann ich das so
direkt verwenden oder ist das edef wichtig und es hat nur zufällig geklappt?

Vielen Dank,

Gruß, Ekkart.
Ulrich D i e z
2012-04-10 10:10:56 UTC
Permalink
Post by Ekkart Kleinod
Post by Ekkart Kleinod
\newwrite\outfile
\immediate\openout\outfile = testblub.txt
\immediate\closeout\outfile
Wenn ich das kopiere, funktioniert das gut.
In meiner Klasse habe ich (aus Versehen) das edef weggelassen, im
Beispiel also direkt
\immediate\write\outfile{Tüte Täte}
verwendet. Das hat auch funktioniert. Meine Frage: kann ich das so
direkt verwenden oder ist das edef wichtig und es hat nur zufällig geklappt?
Ulrike hat ihren Code an die Definition von \***@write
angelehnt, welches nicht \immediate ist, also kein sofortiges
Schreiben auslöst, sondern verzögert schreibt.

"Verzögert Schreiben" bedeutet, dass erst geschrieben wird,
wenn die Output-Routine die Seite ausgibt.
("Verzögertes Schreiben" ist praktisch wenn es bspw darum
geht, die laufende Seitenzahl, bspw fürs Inhaltsverzeichnis/
toc-file, mit auszugeben, da diese erst dann sicher ist, wenn
die Output-Routine die Seite ausgibt.
In dem Fall allerdings, wenn die Output-Routine nicht mehr
anspringt, weil keine Seite mehr ausgegeben wird, werden
die Dinge, die für verzögertes Schreiben noch "auf Halde
liegen", nicht geschrieben, was manchmal für Verwirrung/
Verwunderung sorgt.
Ausserdem wundern sich Leute auch über die Reihenfolge,
in der Dinge in der zu schreibenden Datei auftauchen, wenn
beim Schreiben in die selbe Datei sowohl \write als
auch \immediate\write verwendet werden.)

"Sofortiges Schreiben", also mit \immediate, bedeutet, dass
sofort geschrieben wird, wenn TeX die \write-Anweisung
verarbeitet.

Beim Schreiben (sowohl \immediate als auch verzögert)
werden expandierbare Token expandiert bis nur noch
nicht-expandierbare Token vorliegen. Im Jargon wird
dazu auch "fully evaluated" oder "vollständig expandiert"
gesagt.

Diese nicht-expandierbaren Token werden dann nach den
bei TeX fürs Schreiben solcher Token jeweils geltenden
Regeln geschrieben.

Die Frage ist nun, wann dieses vollständige Expandieren
passiert: Das passiert immer erst dann, wenn TeX sich
anschickt, tatsächlich zu schreiben.

Bei sofortigen Schreiben passiert es also sobald die
\immediate\write-Anweisung verarbeitet wird. Bei verzögertem
Schreiben passiert es erst, wenn die Output-Routine
die Seite ausgibt.

Manche der zu schreibenden Token könnten in der
Zeitspanne, die zwischen der \***@write-Anweisung
bzw der \write-Anweisung ohne \immediate
und dem Ausgeben der Seite liegt, umdefiniert werden.
Dann expandieren sie zum Zeitpunkt des Schreibens
zu etwas anderem als zum Zeitpunkt, zu dem TeX die
\***@write-Anweisung im Quelltext "angetroffen"
hat und man bekommt unter Umständen eine andere
Zeichensequenz geschrieben als man eigentlich
geschrieben haben wollte.

Um dem wenigstens ein bisschen vorzubeugen, ist
in \***@write ein \edef eingebaut, welches dafür
sorgt, dass vor dem Aufruf der \write-Anweisung für
das verzögerte Schreiben die zu schreibenden Token
sofort beim Antreffen der \***@write-Anweisung
so weit wie möglich expandiert/evaluiert werden.
Der \protect-Mechanismus sorgt dabei dafür, dass
auch hier Token wie \thepage nicht expandiert werden,
was zur Folge hat, dass sie erst dann expandiert/evaluiert
werden, wenn beim Ausgeben der Seite der bis dahin
verzögerte Schreibvorgang dann tatsächlich stattfindet.

(Das alles lässt sich an folgendem Beispiel bzw dem sich
dabei ergebenden testblub.txt nett illistrieren:

\documentclass{article}
\newwrite\outfile
\immediate\openout\outfile = testblub.txt
\newcommand\Schreibtext{Das ist der Text vor dem Umdefinieren.}%
\begin{document}
\write\outfile{Erste write-Anweisung: \Schreibtext}%
\makeatletter
\***@write\outfile{}{Zweite write-Anweisung (protected): \Schreibtext}%
\makeatother
\immediate\write\outfile{Dritte write-Anweisung (immediate): \Schreibtext}%
\renewcommand\Schreibtext{Das ist der Text nach dem Umdefinieren.}%
\null\clearpage %output-Routine muss sein
% %für die ersten beiden \write-Anweisungem
\write\outfile{Vierte write-Anweisung (wird nicht ausgefuehrt weil nicht
immediate und keine output-Routine mehr laeuft): \Schreibtext}%
\makeatletter
\***@write\outfile{}{Fuenfte write-Anweisung (wird ebenfalls nicht
ausgefuehrt weil nicht immediate und keine output-Routine
mehr laeuft): \Schreibtext}%
\makeatother
\immediate\write\outfile{Sechste write-Anweisung (immediate): \Schreibtext}%
\end{document}


)


Beim sofortigen Schreiben wird aber sowieso sofort
vollständig expandiert/evaluiert, sodass, wenn ich
das richtig sehe, es nur darauf ankommt,
\protect = \@***@protect zu setzen
und man das \edef bzw ein \(e)def-Assignment
in deinem Spezialfall weglassen kann:

\documentclass{article}
\usepackage[utf8]{inputenc}
\newwrite\outfile
\immediate\openout\outfile = testblub.txt
\makeatletter
\let\protect\@***@protect
\makeatother
\immediate\write\outfile{Tüte Täte}
\immediate\closeout\outfile
\stop


Andererseits lehrt die Erfahrung, dass ich öftermal was
nicht bedenke und Fehlerhaftes in dieser Newsgroup
abliefere, während Ulrike weiss, was sie tut.


Theoretisch könnte man auch \write lokal, dh
in einer Gruppe, patchen und auch fürs sofortige
Schreiben \***@write verwenden:

\documentclass{article}
\usepackage[utf8]{inputenc}
\newwrite\outfile
\immediate\openout\outfile = testblub.txt
\begingroup
\let\savedwrite=\write
\def\write{\immediate\savedwrite}%
\makeatletter
\***@write\outfile{}{Tüte Täte}%
\endgroup
\immediate\closeout\outfile
\stop


Ulrich
Ulrike Fischer
2012-04-10 10:16:37 UTC
Permalink
Post by Ulrich D i e z
Post by Ekkart Kleinod
In meiner Klasse habe ich (aus Versehen) das edef weggelassen, im
Beispiel also direkt
\immediate\write\outfile{Tüte Täte}
verwendet. Das hat auch funktioniert. Meine Frage: kann ich das so
direkt verwenden oder ist das edef wichtig und es hat nur zufällig geklappt?
Andererseits lehrt die Erfahrung, dass ich öftermal was
nicht bedenke und Fehlerhaftes in dieser Newsgroup
abliefere, während Ulrike weiss, was sie tut.
Nein, ich kenne nicht alle Feinheiten von \write. Ich habe schlicht
die Definition kopiert und geändert und mir nicht wirklich Gedanken
gemacht, ob man sie vereinfachen kann.
--
Ulrike Fischer
Ulrich D i e z
2012-04-11 12:40:08 UTC
Permalink
Post by Ulrich D i e z
Um dem wenigstens ein bisschen vorzubeugen, ist
sorgt, dass vor dem Aufruf der \write-Anweisung für
das verzögerte Schreiben die zu schreibenden Token
so weit wie möglich expandiert/evaluiert werden.
Der \protect-Mechanismus sorgt dabei dafür, dass
auch hier Token wie \thepage nicht expandiert werden,
was zur Folge hat, dass sie erst dann expandiert/evaluiert
werden, wenn beim Ausgeben der Seite der bis dahin
verzögerte Schreibvorgang dann tatsächlich stattfindet.
Korrektur:

Der \protect-Mechanismus sorgt dafür, dass bspw
robuste Makros, die bspw per \DeclareRobustCommand
definiert wurden bzw per \protect geschützte Token
bzw robuste active-character-Token, wie sie bei
inputenc verwendet werden, nicht oder in Abhängigkeit
von der momentan gesetzten Definition des \protect-Token
expandiert werden.

Das zweite Argument von \***@write wird
genutzt, um darin enthaltenen Code innerhalb der
von \***@write enthaltenen Gruppe vor dem
\edef auszuführen. Man kann es nuzen, um bestimmte
Token lokal gleich \relax zu setzen, sodass sie während
des \edef nicht expandiert werden sondern unverändert
bleiben.

Das Gleichsetzen von \thepage mit \relax in dieser
Gruppe/diesem local scope ist in \***@write
allerdings schon "hart eincodiert".
Post by Ulrich D i e z
Theoretisch könnte man auch \write lokal, dh
in einer Gruppe, patchen und auch fürs sofortige
\documentclass{article}
\usepackage[utf8]{inputenc}
\newwrite\outfile
\immediate\openout\outfile = testblub.txt
\begingroup
\let\savedwrite=\write
\def\write{\immediate\savedwrite}%
\makeatletter
\endgroup
\immediate\closeout\outfile
\stop
Das war natürlich extrem idiotisch von mir, denn da der Code im
zweiten Argument von \***@write innerhalb einer Gruppe
ausgeführt wird, kann man kann das zweite Argument von
\***@write auch direkt nutzen, um \write lokal zu patchen:

\documentclass{article}
\usepackage[utf8]{inputenc}
\newwrite\outfile
\immediate\openout\outfile = testblub.txt
\makeatletter
\***@write\outfile{%
\let\savedwrite=\write
\def\write{\immediate\savedwrite}%
}{Tüte Täte}%
\immediate\closeout\outfile
\stop

Oder gleich ein eigenes Makro dafür:

\documentclass{article}
\usepackage[utf8]{inputenc}
\newwrite\outfile
\immediate\openout\outfile = testblub.txt
\makeatletter
\newcommand\***@write[2]{%
\***@write{#1}{%
\let\savedwrite=\write
\def\write{\immediate\savedwrite}%
#2}%
}%
\***@write\outfile{}{Tüte Täte}%
\immediate\closeout\outfile
\stop



\***@write hat dann die gleiche
Syntax wie \***@write, bloss wird sofort
geschrieben.

Ulrich
Markus Kohm
2012-04-11 15:43:08 UTC
Permalink
Post by Ulrich D i e z
geschrieben.
In scrlfile gibt es übrigens \***@immediate@write.

Gruß
Markus
Ekkart Kleinod
2012-04-22 21:29:02 UTC
Permalink
[viele hilfreiche Erklärungen]
nochmal Danke für die Erläuterungen, ich kann derzeit nicht so schnell
antworten, wie ich das lese und nutze, daher jetzt erst mein Dank.

Auch Danke an Ulrike und Markus, Ihr drei habt mir richtig weitergeholfen.

Gruß, Ekkart.

Ulrich D i e z
2012-03-30 14:10:05 UTC
Permalink
Post by Ekkart Kleinod
ich habe folgendes Problem: ich schreibe während des LaTeX-Laufs
Informationen in eine Datei (konkret: die Liste der Änderungen) und will
sie wieder einlesen.
Je nach benutzter TeX-Plattform könnte es evtl sein, dass
irgendwelche "TeX-Character-Translation"-Dateien (tcx-files)
nachbearbeitet werden müssen, damit TeX beim Schreiben von
Zeichen in externe Dateien nicht die ^^-Notation verwendet.
(Mehr dazu unter:
http://docs.miktex.org/manual/texfeatures.html)

Du inputenc verwendet wird, vermute ich aber, dass das Problem
eher woanders liegt und irgendwelche aktiven Character beim
unverzögerten Schreiben zur Unzeit expandiert werden.

Um dem abzuhelfen würde ich beim Schreiben der externen Datei
mittels des \addtocontents-\@starttoc-Mechanismus des
LaTeX2e-Kernel über die aux-Datei gehen.

Der Vorteil dabei ist, dass man ab dem zweiten LaTeX-Lauf
während des LaTeX-Laufes jederzeit auf die fragliche Datei
zugreifen kann -- wenn man möchte auch mehrmals, weil sie ja
erst am Ende des jeweiligen LaTeX-Laufes, wenn die während des
LaTeX-Laufes neu geschriebene aux-Datei ausgelesen wird,
gelöscht und neu geschrieben wird.

Der besagte Mechanismus bzw das \addtocontents-Makro arbeitet
übrigens intern mit \***@write, sodass man sich nicht um
Expansionsverhinderung kümmern mus. Allerdings ist es erforderlich,
dass die \output-Routine "ansprimgt", damit das verzögerte Schreiben
auch stattfindet.

Beim Auslesen der Datei kommt es darauf an, wie die geschriebenen
Daten verarbeitet werden sollen:

Für den Fall, dass die Daten alle "auf einen Rutsch" verarbeitet werden
sollen, würde ich einfach den \addtocontents-\@starttoc-Mechanismus
verwenden und Aufrufe für Zeilenverarbeitende Makros wie \testchopline
oder Makros, welche dazu dienen, \testchopline aufzurufen, mit in die
externe Datei schreiben.

Das könnte dann wie folgt aussehen:

\documentclass[11pt, a4paper, ngerman]{article}
\usepackage[ngerman]{babel}
\usepackage[utf8]{inputenc}

\makeatletter
\DeclareRobustCommand\testlineinbox[1]{%
\noindent\fbox{\vtop{\noindent\testchopline#1\\}}%
}%
\@ifdefinable\testchopline{%
\def\testchopline#1;#2\\{%
Erster Teil: #1\\%
Zweiter Teil: #2%
}%
}%

\newcommand\testfileausgabe{\@starttoc{tst}}%

% Es sollte mindestens einmal die \output-Routine laufen
% damit \addtocontents-Einträge auch geschrieben werden:
\AtEndDocument{\clearpage}
\makeatother

\begin{document}

bla bla bla

\addtocontents{tst}{\testlineinbox{Test1;Test2}}

Ausgabe der Datei \jobname.tst:

\testfileausgabe

bla bla bla

\addtocontents{tst}{\testlineinbox{Täst1;Täst2}}

bla bla bla
\end{document}



Sofern das nicht geht, bspw, weil die externen Dateien nicht von
LaTeX stammen und die ausgebende Software keine
LaTeX-Kontrollsequenzen dazuschreiben kann, oder wenn freier
Zugriff auf einzelne Zeilen gegeben sein soll, wird es ein wenig
komplexer weil man auf das Erreichen des Dateiendes hin
übberwachen sollte und per \addtocontents auf jeden Fall ein
Zeilenvorschub in eine Datei geschrieben wird.

Beim Einlesen wird von TeX ein einsamer Zeilenvorschub als leere
Zeile betrachtet, sodass man beim Verarbeiten von Textzeilen
mittels \read immer den Sonderfall einer leeren Textzeile vor
dem Erreichen des Dateiendes im Auge haben sollte.

Ausserdem steht im TeXBook einiges zu dem Integer-Parameter
\endlinechar.
Ich empfehle dringendst, vor dem Lesen mit \read diesem
Parameter einen Nicht-positiven Wert zu geben. Ansonsten hängt
TeX nämlich beim Lesen einer Zeile selbiger noch einen Character
an, dessen charcode dem Wert des \endlinechar-Parameter
entspricht, in der Regel Nummer 13, also Character Nummer 13 =
Return = ^^M, während dieser Character 13 wiederum in der Regel
den catcode 5 (end of line) hat, sodass man nicht weiss, ob man
beim Tokenizen dafür dann ein \par-Token oder ein Space-Token
oder gar kein Token bekommt (und somit auch nicht weiss, wie der
Delimiter eines Makro mit begrenztem Argument für den jeweiligen
Fall grade aussehen müsste), weil das bei catcode-5-Character
davon abhängt, in welchem Zustand (new-line oder mid-line oder
skipping blanks) der Leseapparatus von TeX gerade ist.

Ich würde ich etwas wie folgt versuchen:


\documentclass[11pt, a4paper, ngerman]{article}
\usepackage[ngerman]{babel}
\usepackage[utf8]{inputenc}

\makeatletter

%Hilfsmakros, nicht User-Ebene

\newcommand\@ifnull[3]{%
\romannumeral\iffalse{\fi\expandafter\@secondoftwo\expandafter
{\expandafter{\string#1}\expandafter\@secondoftwo\string}%
\expandafter\@firstoftwo\expandafter{\iffalse}\fi0 #3}{0 #2}%
} %
\newcommand\PassFirstToSecond[2]{#2{#1}}%

% User-Makro zum Lese-Öffnen einer Datei:
% \openfile{<Extension>}
% (Es wird angenommen, dass alle Dateien nach dem Schema
% \jobname.<Extension> benannt sind.)

\newcommand*\openfile[1]{%
\begingroup
\let\***@path\@undefined
\IfFileExists{\jobname.#1}{%
\endgroup
\@ifundefined{inp@#1}{%
\expandafter\newread\csname inp@#1\endcsname%
}{}%
\expandafter\ifeof\csname inp@#1\endcsname
\immediate\openin\csname inp@#1\endcsname\jobname.#1\relax
\else
%Datei kann nicht geöffnet werden weil das \read-handle
%schon für eine bereits geöffnete Datei benutzt wird,
%evtl hier Fehlermeldung einbauen.
\fi
}{\endgroup}%
}%

% User-Makro zum Schliessen einer lesegeöffneten Datei:
% \closefile{<Extension>}
% (Es wird angenommen, dass alle Dateien nach dem Schema
% \jobname.<Extension> benannt sind.)

\newcommand*\closefile[1]{%
\@ifundefined{inp@#1}{}{\immediate\closein\csname inp@#1\endcsname}%
}%

% User-Makro zum Lesen der nächsten Zeile einer lesegeöffneten
% Datei:
% \processfileline{<Extension>}{<Makro>}%
% (Es wird angenommen, dass alle Dateien nach dem Schema
% \jobname.<Extension> benannt sind.)
% Die gelesene Zeile wird als in geschweifte Klammern
% eingefasstes Argument an <Makro> übergeben, sodass sie
% von <Makro> weiterverarbeitet werden kann.

\newcommand*\processfileline[2]{%
\@ifundefined{inp@#1}{}{%
\expandafter\ifeof\csname inp@#1\endcsname
\expandafter\@gobble
\else
\expandafter\@firstofone
\fi
{%
\begingroup
\endlinechar=-1\relax
\immediate\read\csname inp@#1\endcsname to\@***@line
\expandafter\PassFirstToSecond
\expandafter{\@***@line}{\endgroup#2}%
}%
}%
}%

% User-Makro zum Lesen ener bestimmten Zeile einer dafür zum
% Lesen zu öffnenden Datei:
% \processfileline{<Extension>}{<Makro>}{<Zeilennummer>}%
% (Es wird angenommen, dass alle Dateien nach dem Schema
% \jobname.<Extension> benannt sind.)
% Es wird, sofern existent, die <Zeilennummer>-te Zeile
% der Datei \jobname.<Extension> gelesen und als in
% geschweifte Klammern eingefasstes Argument an <Makro>
% übergeben, sodass sie von <Makro> weiterverarbeitet
% werden kann.


\newcommand\ProcessFileLineNumber[3]{%
\ifnum1>#3 %
% evtl Fehlermeldung weil Zeilenangaben größer 0 sein müssen
\expandafter\@gobble
\else
\expandafter\@firstofone
\fi
{%
\begingroup
\let\***@path\@undefined
\IfFileExists{\jobname.#1}{%
\endgroup
\immediate\openin\@inputcheck\jobname.#1\relax
\ProcessFileLineNumberloop{#3}{#1}{#2}%
\immediate\closein\@inputcheck
}{\endgroup}%
}%
}%

\newcommand\ProcessFileLineNumberloop[3]{%
\ifeof\@inputcheck
\expandafter\@gobble
\else
\expandafter\@firstofone
\fi
{%
\begingroup
\endlinechar=-1\relax
\immediate\read\@inputcheck to\@***@line
\ifnum1=#1 %
\expandafter\@firstoftwo
\else
\expandafter\endgroup\expandafter\@secondoftwo
\fi
{%
\expandafter\PassFirstToSecond
\expandafter{\@***@line}{\endgroup#3}%
}{%
\expandafter\ProcessFileLineNumberloop{%
\expandafter\number\numexpr#1-1\relax}{#2}{#3}%
}%
}%
}%


% Spezifisches Makro zum Verarbeiten von Zeilen, die aus
% Dateien mit der Extension ".tst" kommen - #1 beinhaltet
% die gelesene Zeile:

\newcommand\processnextlineoftstfile[1]{%
\@ifnull{#1}{}{%
\testchopline#1\\%
Erster Teil: \first\\%
Zweiter Teil: \second
}%
}%

\@ifdefinable\testchopline{%
\def\testchopline#1;#2\\{%
\def\first{#1}%
\def\second{#2}%
}%
}%

% Es soll nach dem LaTeX-Lauf aus den per \addtocontents
% bewerkstelligten aux-file-Einträgen eine tst-Datei
% erzeugt werden:
\AtEndDocument{%
\begingroup\let\@input\@gobble\@starttoc{tst}\endgroup
}
% Es sollte mindestens einmal die \output-Routine laufen
% damit \addtocontents-Einträge auch geschrieben werden:
\AtEndDocument{\clearpage}
\makeatother

\begin{document}

%Schreibe Zeile 1:
\addtocontents{tst}{Test1;Test2}

\openfile{tst}
Zeile 1:
\fbox{\vtop{\noindent
\processfileline{tst}{\processnextlineoftstfile}%
}}

Zeile 2:
\fbox{\vtop{\noindent
\processfileline{tst}{\processnextlineoftstfile}%
}}

%Schreibe Zeile 2:
\addtocontents{tst}{Täst1;Täst2}

% Das hier produziert eine leere \fbox, weil Zeile 3
% in \jobname.tst nicht existiert:
Zeile 3:
\fbox{\vtop{\noindent
\processfileline{tst}{\processnextlineoftstfile}%
}}
\closefile{tst}


Nochmal Zeile 2:
\fbox{\vtop{\noindent
\ProcessFileLineNumber{tst}{\processnextlineoftstfile}{2}%
}}

Nochmal Zeile 1:
\fbox{\vtop{\noindent
\ProcessFileLineNumber{tst}{\processnextlineoftstfile}{1}%
}}

Nochmal Zeile 3:
\fbox{\vtop{\noindent
\ProcessFileLineNumber{tst}{\processnextlineoftstfile}{3}%
}}

\end{document}




Ulrich
Ulrich D i e z
2012-03-30 14:30:31 UTC
Permalink
Post by Ekkart Kleinod
ich habe folgendes Problem: ich schreibe während des LaTeX-Laufs
Informationen in eine Datei (konkret: die Liste der Änderungen) und will
sie wieder einlesen.
Je nach benutzter TeX-Plattform könnte es evtl sein, dass
irgendwelche "TeX-Character-Translation"-Dateien (tcx-files)
nachbearbeitet werden müssen, damit TeX beim Schreiben von
Zeichen in externe Dateien nicht die ^^-Notation verwendet.
(Mehr dazu unter:
http://docs.miktex.org/manual/texfeatures.html)

Du inputenc verwendet wird, vermute ich aber, dass das Problem
eher woanders liegt und irgendwelche aktiven Character beim
unverzögerten Schreiben zur Unzeit expandiert werden.

Um dem abzuhelfen würde ich beim Schreiben der externen Datei
mittels des \addtocontents-\@starttoc-Mechanismus des
LaTeX2e-Kernel über die aux-Datei gehen.

Der Vorteil dabei ist, dass man ab dem zweiten LaTeX-Lauf
während des LaTeX-Laufes jederzeit auf die fragliche Datei
zugreifen kann -- wenn man möchte auch mehrmals, weil sie ja
erst am Ende des jeweiligen LaTeX-Laufes, wenn die während des
LaTeX-Laufes neu geschriebene aux-Datei ausgelesen wird,
gelöscht und neu geschrieben wird.

Der besagte Mechanismus bzw das \addtocontents-Makro arbeitet
übrigens intern mit \***@write, sodass man sich nicht um
Expansionsverhinderung kümmern mus. Allerdings ist es erforderlich,
dass die \output-Routine "ansprimgt", damit das verzögerte Schreiben
auch stattfindet.

Beim Auslesen der Datei kommt es darauf an, wie die geschriebenen
Daten verarbeitet werden sollen:

Für den Fall, dass die Daten alle "auf einen Rutsch" verarbeitet werden
sollen, würde ich einfach den \addtocontents-\@starttoc-Mechanismus
verwenden und Aufrufe für Zeilenverarbeitende Makros wie \testchopline
oder Makros, welche dazu dienen, \testchopline aufzurufen, mit in die
externe Datei schreiben.

Das könnte dann wie folgt aussehen:

\documentclass[11pt, a4paper, ngerman]{article}
\usepackage[ngerman]{babel}
\usepackage[utf8]{inputenc}

\makeatletter
\DeclareRobustCommand\testlineinbox[1]{%
\noindent\fbox{\vtop{\noindent\testchopline#1\\}}%
}%
\@ifdefinable\testchopline{%
\def\testchopline#1;#2\\{%
Erster Teil: #1\\%
Zweiter Teil: #2%
}%
}%

\newcommand\testfileausgabe{\@starttoc{tst}}%

% Es sollte mindestens einmal die \output-Routine laufen
% damit \addtocontents-Einträge auch geschrieben werden:
\AtEndDocument{\clearpage}
\makeatother

\begin{document}

bla bla bla

\addtocontents{tst}{\testlineinbox{Test1;Test2}}

Ausgabe der Datei \jobname.tst:

\testfileausgabe

bla bla bla

\addtocontents{tst}{\testlineinbox{Täst1;Täst2}}

bla bla bla
\end{document}



Sofern das nicht geht, bspw, weil die externen Dateien nicht von
LaTeX stammen und die ausgebende Software keine
LaTeX-Kontrollsequenzen dazuschreiben kann, oder wenn freier
Zugriff auf einzelne Zeilen gegeben sein soll, wird es ein wenig
komplexer weil man auf das Erreichen des Dateiendes hin
übberwachen sollte und per \addtocontents auf jeden Fall ein
Zeilenvorschub in eine Datei geschrieben wird.

Beim Einlesen wird von TeX ein einsamer Zeilenvorschub als leere
Zeile betrachtet, sodass man beim Verarbeiten von Textzeilen
mittels \read immer den Sonderfall einer leeren Textzeile vor
dem Erreichen des Dateiendes im Auge haben sollte.

Ausserdem steht im TeXBook einiges zu dem Integer-Parameter
\endlinechar.
Ich empfehle dringendst, vor dem Lesen mit \read diesem
Parameter einen Nicht-positiven Wert zu geben. Ansonsten hängt
TeX nämlich beim Lesen einer Zeile selbiger noch einen Character
an, dessen charcode dem Wert des \endlinechar-Parameter
entspricht, in der Regel Nummer 13, also Character Nummer 13 =
Return = ^^M, während dieser Character 13 wiederum in der Regel
den catcode 5 (end of line) hat, sodass man nicht weiss, ob man
beim Tokenizen dafür dann ein \par-Token oder ein Space-Token
oder gar kein Token bekommt (und somit auch nicht weiss, wie der
Delimiter eines Makro mit begrenztem Argument für den jeweiligen
Fall grade aussehen müsste), weil das bei catcode-5-Character
davon abhängt, in welchem Zustand (new-line oder mid-line oder
skipping blanks) der Leseapparatus von TeX gerade ist.

Ich würde ich etwas wie folgt versuchen:


\documentclass[11pt, a4paper, ngerman]{article}
\usepackage[ngerman]{babel}
\usepackage[utf8]{inputenc}

\makeatletter

%Hilfsmakros, nicht User-Ebene

\newcommand\@ifnull[3]{%
\romannumeral\iffalse{\fi\expandafter\@secondoftwo\expandafter
{\expandafter{\string#1}\expandafter\@secondoftwo\string}%
\expandafter\@firstoftwo\expandafter{\iffalse}\fi0 #3}{0 #2}%
} %
\newcommand\PassFirstToSecond[2]{#2{#1}}%

% User-Makro zum Lese-Öffnen einer Datei:
% \openfile{<Extension>}
% (Es wird angenommen, dass alle Dateien nach dem Schema
% \jobname.<Extension> benannt sind.)

\newcommand*\openfile[1]{%
\begingroup
\let\***@path\@undefined
\IfFileExists{\jobname.#1}{%
\endgroup
\@ifundefined{inp@#1}{%
\expandafter\newread\csname inp@#1\endcsname%
}{}%
\expandafter\ifeof\csname inp@#1\endcsname
\immediate\openin\csname inp@#1\endcsname\jobname.#1\relax
\else
%Datei kann nicht geöffnet werden weil das \read-handle
%schon für eine bereits geöffnete Datei benutzt wird,
%evtl hier Fehlermeldung einbauen.
\fi
}{\endgroup}%
}%

% User-Makro zum Schliessen einer lesegeöffneten Datei:
% \closefile{<Extension>}
% (Es wird angenommen, dass alle Dateien nach dem Schema
% \jobname.<Extension> benannt sind.)

\newcommand*\closefile[1]{%
\@ifundefined{inp@#1}{}{\immediate\closein\csname inp@#1\endcsname}%
}%

% User-Makro zum Lesen der nächsten Zeile einer lesegeöffneten
% Datei:
% \processfileline{<Extension>}{<Makro>}%
% (Es wird angenommen, dass alle Dateien nach dem Schema
% \jobname.<Extension> benannt sind.)
% Die gelesene Zeile wird als in geschweifte Klammern
% eingefasstes Argument an <Makro> übergeben, sodass sie
% von <Makro> weiterverarbeitet werden kann.

\newcommand*\processfileline[2]{%
\@ifundefined{inp@#1}{}{%
\expandafter\ifeof\csname inp@#1\endcsname
\expandafter\@gobble
\else
\expandafter\@firstofone
\fi
{%
\begingroup
\endlinechar=-1\relax
\immediate\read\csname inp@#1\endcsname to\@***@line
\expandafter\PassFirstToSecond
\expandafter{\@***@line}{\endgroup#2}%
}%
}%
}%

% User-Makro zum Lesen ener bestimmten Zeile einer dafür zum
% Lesen zu öffnenden Datei:
% \processfileline{<Extension>}{<Makro>}{<Zeilennummer>}%
% (Es wird angenommen, dass alle Dateien nach dem Schema
% \jobname.<Extension> benannt sind.)
% Es wird, sofern existent, die <Zeilennummer>-te Zeile
% der Datei \jobname.<Extension> gelesen und als in
% geschweifte Klammern eingefasstes Argument an <Makro>
% übergeben, sodass sie von <Makro> weiterverarbeitet
% werden kann.


\newcommand\ProcessFileLineNumber[3]{%
\ifnum1>#3 %
% evtl Fehlermeldung weil Zeilenangaben größer 0 sein müssen
\expandafter\@gobble
\else
\expandafter\@firstofone
\fi
{%
\begingroup
\let\***@path\@undefined
\IfFileExists{\jobname.#1}{%
\endgroup
\immediate\openin\@inputcheck\jobname.#1\relax
\ProcessFileLineNumberloop{#3}{#1}{#2}%
\immediate\closein\@inputcheck
}{\endgroup}%
}%
}%

\newcommand\ProcessFileLineNumberloop[3]{%
\ifeof\@inputcheck
\expandafter\@gobble
\else
\expandafter\@firstofone
\fi
{%
\begingroup
\endlinechar=-1\relax
\immediate\read\@inputcheck to\@***@line
\ifnum1=#1 %
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{%
\expandafter\PassFirstToSecond
\expandafter{\@***@line}{\endgroup#3}%
}{%
\endgroup
\expandafter\ProcessFileLineNumberloop\expandafter{%
\number\numexpr#1-1\relax}{#2}{#3}%
}%
}%
}%


% Spezifisches Makro zum Verarbeiten von Zeilen, die aus
% Dateien mit der Extension ".tst" kommen - #1 beinhaltet
% die gelesene Zeile:

\newcommand\processnextlineoftstfile[1]{%
\@ifnull{#1}{}{%
\testchopline#1\\%
Erster Teil: \first\\%
Zweiter Teil: \second
}%
}%

\@ifdefinable\testchopline{%
\def\testchopline#1;#2\\{%
\def\first{#1}%
\def\second{#2}%
}%
}%

% Es soll nach dem LaTeX-Lauf aus den per \addtocontents
% bewerkstelligten aux-file-Einträgen eine tst-Datei
% erzeugt werden:
\AtEndDocument{%
\begingroup\let\@input\@gobble\@starttoc{tst}\endgroup
}
% Es sollte mindestens einmal die \output-Routine laufen
% damit \addtocontents-Einträge auch geschrieben werden:
\AtEndDocument{\clearpage}
\makeatother

\begin{document}

%Schreibe Zeile 1:
\addtocontents{tst}{Test1;Test2}

\openfile{tst}
Zeile 1:
\fbox{\vtop{\noindent
\processfileline{tst}{\processnextlineoftstfile}%
}}

Zeile 2:
\fbox{\vtop{\noindent
\processfileline{tst}{\processnextlineoftstfile}%
}}

%Schreibe Zeile 2:
\addtocontents{tst}{Täst1;Täst2}

% Das hier produziert eine leere \fbox, weil Zeile 3
% in \jobname.tst nicht existiert:
Zeile 3:
\fbox{\vtop{\noindent
\processfileline{tst}{\processnextlineoftstfile}%
}}
\closefile{tst}


Nochmal Zeile 2:
\fbox{\vtop{\noindent
\ProcessFileLineNumber{tst}{\processnextlineoftstfile}{2}%
}}

Nochmal Zeile 1:
\fbox{\vtop{\noindent
\ProcessFileLineNumber{tst}{\processnextlineoftstfile}{1}%
}}

Nochmal Zeile 3:
\fbox{\vtop{\noindent
\ProcessFileLineNumber{tst}{\processnextlineoftstfile}{3}%
}}

\end{document}




Ulrich
Ekkart Kleinod
2012-04-03 17:33:37 UTC
Permalink
Post by Ulrich D i e z
Um dem abzuhelfen würde ich beim Schreiben der externen Datei
LaTeX2e-Kernel über die aux-Datei gehen.
habe ich auch schon überlegt, ad-hoc war die dirrekte Variante besser,
ich hatte halt keine Umlaute ausprobiert.

Vielen Dank für Deine Ausführungen, muss ich nochmal in Ruhe durchgehen,
da ist viel schwerer Stoff dabei...

Konnte erst jetzt antworten, tut mir leid für die verzögerung.

Gruß, Ekkart.
Loading...