Kodierung und Zeilenumbrüche

In Shift_JIS ist das Zeichen 本 das Bytepaar 96 7B, und 7B ist ASCII {; das Zeichen 表 ist 95 5C, und 5C ist ein Backslash. Wird die Zeichenkodierung verwechselt, verwandelt sich japanischer Fließtext Byte für Byte in LaTeX-Kontrollsequenzen und geschweifte Klammern. Deshalb bedeutet Mojibake in TeX nie bloß „unleserliche Glyphen", und deshalb gehört eine .tex-Datei heute als UTF-8 mit LF gespeichert. Diese Seite zeigt anhand echter Fehlermeldungen, was beim Zusammentreffen mit Resten von Shift_JIS, EUC-JP oder ISO-2022-JP passiert, wie man sie konvertiert und wie sich der Zeichenvorrat von platex und uplatex unterscheidet.

Womit sollte eine .tex-Datei gespeichert werden? UTF-8 und LF

UTF-8 – mit oder ohne BOM – und LF. Unter TeX Live 2024 lesen uplatex, lualatex und xelatex standardmäßig UTF-8, und Git setzt beim Diff UTF-8 und LF voraus. Heikel wird es im Japanischen, weil vor der Verbreitung von Unicode drei zueinander inkompatible Kodierungen gleichzeitig im Einsatz waren: Shift_JIS unter Windows, EUC-JP unter Unix und ISO-2022-JP – der „JIS-Code" – für E-Mail, das durch Sieben-Bit-Kanäle musste. Dasselbe Kanji hatte drei verschiedene Bytefolgen, und keine Betrachtung der Datei sagt sicher, welche vorliegt. Deshalb ist die Kodierung das Erste, was man beim Öffnen eines alten Institutsverzeichnisses verdächtigt.

KodierungEinsatzbereichWo man ihr noch begegnet
UTF-8heutiger Standard (Unicode)einzige Wahl für Neues und Standard von TeX Live
Shift_JISältere Windows-Systeme, DTP, Spielkonsolenalte verteilte Vorlagen, CD-ROM-Beilagen; auch CP932 genannt
EUC-JPältere Unix-Systeme, Rechenzentren.tex- und .sty-Dateien in gemeinsamen Institutsverzeichnissen
ISO-2022-JPE-Mail (der „JIS-Code")Sieben-Bit-Verfahren, das Zeichensätze per Escape-Sequenz umschaltet; aus Mails eingefügte Fragmente

Was passiert, wenn eine Shift_JIS-Datei auf eine UTF-8-Werkzeugkette trifft

Der Bildschirm füllt sich nicht mit unleserlichen Glyphen. Es erscheint eine Kaskade von Fehlern, und der Lauf kippt bei ! Undefined control sequence. Gibt man unter TeX Live 2024 eine Shift_JIS-Datei direkt an uplatex, kommt zuerst ! LaTeX Error: Invalid UTF-8 byte "93., dann ! LaTeX Error: Invalid UTF-8 byte sequence (^^ea^^82̕). Bis hierhin ist alles ehrlich: Meldungen über unzulässige Bytes. Das Problem folgt danach: Die wiedergegebene Zeile lautet l.3 ^^93^^fa^^96{^^8c^^ea^^82̕\^^8e, und man sieht es unmittelbar – das zweite Byte von 本 ist zu { geworden, das zweite Byte von 表 zu \, und beide sind jetzt gültige TeX-Syntax. Das Symptom lautet daher nicht „das Japanische sieht falsch aus", sondern „LaTeX meldet einen Befehl als undefiniert, den ich nie geschrieben habe" – genau deshalb fällt der Verdacht zuletzt auf die Kodierung.

terminal
$ uplatex sjis.tex
! LaTeX Error: Invalid UTF-8 byte "93.
l.3 ^^93
! LaTeX Error: Invalid UTF-8 byte sequence (^^ea^^82̕).
! Undefined control sequence.
l.3 ^^93^^fa^^96{^^8c^^ea^^82̕\^^8e

$ uplatex -kanji=sjis sjis.tex        # tell the engine what it is reading
Output written on sjis.dvi (1 page, 320 bytes).

Der Unfall folgt zwangsläufig aus dem Entwurf von Shift_JIS. Weil das zweite Byte eines Doppelbytezeichens in den ASCII-Bereich 0x400x7E fallen darf, gibt es 52 Zeichen, deren zweites Byte genau 0x5C (Backslash) ist, und 50, deren zweites Byte 0x7B (öffnende geschweifte Klammer) ist. Zur ersten Gruppe gehören 表, 十, ソ, 能, 貼, 暴, 申 und 構, zur zweiten 本, 宮, 施, 旬, 養 und 鶏 – durchweg gewöhnliche Zeichen im laufenden Japanisch. Japanische Entwickler nannten sie die „schlimmen Zeichen" und kämpften jahrelang damit, denn derselbe Unfall traf Shell-Skripte und Konfigurationsdateien ebenso wie TeX. EUC-JP kennt das Problem nicht – seine zweiten Bytes liegen stets bei 0x80 oder darüber –, weshalb EUC-JP im TeX-Umfeld eine Zeit lang bevorzugt wurde.

Shift_JIS nach UTF-8 konvertieren: iconv und nkf

Das klassische japanische Werkzeug ist nkf (Network Kanji Filter), doch es muss nachinstalliert werden – weder macOS noch die meisten Linux-Distributionen bringen es mit. Zuerst iconv versuchen: ein POSIX-Standardwerkzeug, das auf macOS wie auf Linux als /usr/bin/iconv vorhanden ist. iconv -f CP932 -t UTF-8 old.tex > new.tex erledigt die Umwandlung von Shift_JIS nach UTF-8. Ist nkf vorhanden, schreibt nkf -w -Lu --overwrite *.tex viele Dateien in einer Zeile direkt um – das lohnt die Installation, wenn ein ganzes Verzeichnis übernommen wird. In jedem Fall gilt: vorher eine Kopie anlegen oder in Git einchecken. --overwrite zerstört das Original genau so, wie es der Name sagt.

terminal
# iconv -- always present; safest one file at a time
iconv -f CP932  -t UTF-8 old.tex > new.tex     # Shift_JIS -> UTF-8
iconv -f EUC-JP -t UTF-8 old.tex > new.tex     # EUC-JP    -> UTF-8

# whole tree, keeping a backup of every original
for f in *.tex; do cp "$f" "$f.bak"; iconv -f CP932 -t UTF-8 "$f.bak" > "$f"; done

# nkf, if installed: detect first, then convert in place to UTF-8 + LF
nkf -g old.tex
nkf -w -Lu --overwrite *.tex

Bei iconv gilt: den Namen CP932 verwenden, nicht SHIFT_JIS. Beide werden häufig gleichgesetzt und sind es nicht. Lässt man iconv unter macOS Text mit (Wellenstrich) oder (eingekreiste Ziffer) nach SHIFT_JIS wandeln, bricht es mit iconv: iconv(): Illegal byte sequence ab; mit CP932 gelingt es. SHIFT_JIS ist die enge, an JIS X 0208 orientierte Definition und enthält die NEC- und IBM-Erweiterungszeichen nicht – eingekreiste Ziffern, römische Zahlen und dergleichen. Dateien aus der Windows-Welt sind praktisch immer CP932, deshalb ist CP932 die richtige Voreinstellung. Dasselbe gilt für den Rückweg, wenn UTF-8 für ein altes Werkzeug wieder nach Shift_JIS muss.

nkf-OptionWirkungEntsprechung in iconv
-gaktuelle Kodierung und Zeilenende erkennen, nichts konvertierenkein Gegenstück – file oder ein Detektor wie chardetect
-wnach UTF-8 ohne BOM konvertiereniconv -t UTF-8
-s / -e / -jnach Shift_JIS / EUC-JP / ISO-2022-JP konvertiereniconv -t CP932 / -t EUC-JP / -t ISO-2022-JP
-Lu / -Lw / -LmZeilenenden auf LF / CRLF / CR vereinheitlichenkein Gegenstück – sed, dos2unix oder Gits eol=lf
--overwritedie angegebenen Dateien direkt überschreibenkein Gegenstück – iconv schreibt auf die Standardausgabe, also in eine neue Datei umleiten

-kanji=: der Engine die Lesart mitteilen, ohne zu konvertieren

Wer die Datei nicht umschreiben will – oder nicht darf –, teilt es stattdessen der Engine mit. (u)platex versteht -kanji= mit den Werten -kanji=sjis, -kanji=euc, -kanji=jis und -kanji=utf8. Die Shift_JIS-Datei, die als UTF-8 gelesen eine Fehlerwand erzeugte, übersetzt mit uplatex -kanji=sjis sjis.tex schlicht zu Output written on sjis.dvi (1 page, 320 bytes). Das ist jedoch Erste Hilfe. Editor, Git, grep und jede per \input eingebundene Datei gehen weiterhin von UTF-8 aus; sobald sich Kodierungen mischen, folgt der nächste Unfall. Man nutzt es, um ein zugesandtes Manuskript einmal zu übersetzen und lesbar zu machen – und konvertiert danach. lualatex und xelatex kennen -kanji= gar nicht: Sie lesen immer UTF-8.

platex und uplatex: dieselbe Binärdatei, zwei Zeichenwelten

Der Unterschied liegt im Zeichenvorrat, nicht im Programm. Ruft man unter TeX Live 2024 platex --version und uplatex --version auf, meldet sich beides Mal e-upTeX 3.141592653-p4.1.1-u1.30-230214-2.6dieselbe Binärdatei. Nur die Klammer unterscheidet sich: (utf8.euc) bei platex, (utf8.uptex) bei uplatex. Sie laden verschiedene Formate, und das Format bestimmt, wie Zeichen intern gehalten werden. Die Folge ist handfest: Setzt man 髙 (U+9AD9, eine in japanischen Familiennamen häufige Variante von 高) in ein Dokument, bricht platex mit ! LaTeX Error: Unicode character ^^e9^^ab^^99 (U+9AD9) ab, während uplatex es kommentarlos setzt. Einfaches platex bleibt im Repertoire von JIS X 0208 eingeschlossen; alles darüber hinaus wird an der Tür abgewiesen. Für ein neues Dokument gibt es keinen Grund mehr, platex zu wählen. Mit uplatex als Vorgabe gehen 髙, 𠮟 und die Varianten in einer Namensliste allesamt durch.

terminal
$ platex --version | head -1
e-upTeX 3.141592653-p4.1.1-u1.30-230214-2.6 (utf8.euc) (TeX Live 2024)
$ uplatex --version | head -1
e-upTeX 3.141592653-p4.1.1-u1.30-230214-2.6 (utf8.uptex) (TeX Live 2024)

$ platex takashima.tex          # the document contains 髙 (U+9AD9)
! LaTeX Error: Unicode character ^^e9^^ab^^99 (U+9AD9)
$ uplatex takashima.tex
Output written on takashima.dvi (1 page, 304 bytes).

Zeilenenden und BOM: Brechen LF, CRLF und CR wirklich etwas?

LaTeX selbst nimmt alle drei klaglos an. Übergibt man uplatex eine mit \r\n (CRLF, Windows) geschriebene .tex-Datei oder eine, die mit einem UTF-8-BOM (EF BB BF) beginnt, übersetzen unter TeX Live 2024 sowohl uplatex als auch lualatex ohne eine einzige Warnung. Die verbreitete Rede, ein BOM hinterlasse am Dokumentanfang ein unsichtbares Zeichen, trifft auf diese beiden Engines heute nicht zu. Es leidet nicht LaTeX, sondern das Werkzeugumfeld. Eine Datei mit gemischtem CRLF und LF erscheint in Git als vollständig geändert, was jede Durchsicht unmöglich macht. Eine .sty mit übrig gebliebenen \r am Zeilenende kann einen Zeilenendanker in grep aushebeln. Die Vereinheitlichung auf LF ist deshalb eine Entscheidung für die Zusammenarbeit, nicht für den Satz. Unter Git ist eine Zeile in .gitattributes die zuverlässigste Lösung; sie gleicht beim Auschecken auch die Unterschiede zwischen den Rechnern der Beteiligten aus.

terminal
# .gitattributes -- normalise on checkin, hand out LF on checkout
*.tex text eol=lf
*.sty text eol=lf
*.bib text eol=lf
*.pdf binary

# one-off cleanup of a file that arrived with CRLF
sed -i.bak $'s/\r$//' old.tex
  • Alles, alt wie neu, auf UTF-8 + LF vereinheitlichen. Es ist die Vorgabe aller drei TeX-Live-2024-Engines und passt zu Gits Erwartungen.
  • Konvertierungen mit iconv -f CP932 -t UTF-8 beginnen. CP932 angeben, nicht SHIFT_JIS, sonst scheitern eingekreiste Ziffern und der Wellenstrich.
  • Ist nkf installiert, ist nkf -w -Lu --overwrite *.tex am schnellsten – es liegt aber weder macOS noch den meisten Linux-Distributionen bei.
  • Vor dem Konvertieren kopieren oder in Git einchecken. --overwrite lässt sich nicht rückgängig machen.
  • Neue Dokumente mit uplatex (oder lualatex) beginnen. Einfaches platex bricht bei Zeichen außerhalb JIS X 0208 ab, etwa 髙 (U+9AD9).
  • -kanji=sjis ist Erste Hilfe. Sobald das Dokument lesbar ist, die Datei selbst nach UTF-8 konvertieren.