Shift_JIS で「本」という字を書くと、バイト列は 96 7B になり、後ろの 7B は ASCII の { です(「表」は 95 5C で、5C は バックスラッシュ)。つまり 文字コード(エンコーディング) を取り違えた瞬間、日本語の本文が LaTeX にとっての制御綴と波括弧に化けます。文字化けが「読めない字が出る」で済まないのはこのためで、いまの .tex を UTF-8 + LF で保存すべき理由もここにあります。このページでは、Shift_JIS・EUC-JP・ISO-2022-JP の残骸に出くわしたときに何が起き、どう直すか、そして platex と uplatex で扱える文字がどう違うかを、実際のエラー本文とともに見ていきます。
.tex は何で保存すべきか — UTF-8 と LF
UTF-8(BOM の有無は問わない)と LF です。TeX Live 2024 の uplatex・lualatex・xelatex はどれも既定で UTF-8 を読み、Git も UTF-8 と LF を前提に差分を作ります。日本語圏でここが厄介なのは、Unicode が普及する前に 三つの互換性のないエンコーディングが並立していた からです。Windows は Shift_JIS、Unix は EUC-JP、メールは 7 ビットの経路を通すために ISO-2022-JP(いわゆる「JIS コード」)。同じ漢字が三通りのバイト列を持ち、しかもファイルの中身だけを見て確実に見分けることはできません。だからこそ、古い研究室のディレクトリを開けたときにまず疑うべきはエンコーディングなのです。
| エンコーディング | 使われた場所 | いま出会う場面 |
|---|---|---|
UTF-8 | 現在の標準(Unicode) | 新規はこれ一択。TeX Live の既定でもある |
Shift_JIS | 旧 Windows、DTP、ゲーム機 | 配布された古いテンプレート、CD-ROM 付録、CP932 とも呼ばれる |
EUC-JP | 旧 Unix、大学の計算機センター | 研究室の共有ディレクトリに残る .tex や .sty |
ISO-2022-JP | 電子メール(「JIS コード」) | エスケープ列で文字集合を切り替える 7 ビット方式。メール由来の断片 |
Shift_JIS のファイルを UTF-8 のまま開くと何が起きるか
画面が読めない字で埋まる、のではありません。エラーが立て続けに出て、最後に ! Undefined control sequence. で転びます。TeX Live 2024 の uplatex に Shift_JIS のファイルをそのまま食わせると、まず ! LaTeX Error: Invalid UTF-8 byte "93. が出て、続けて ! LaTeX Error: Invalid UTF-8 byte sequence (^^ea^^82̕). と積み重なります。ここまでは「不正なバイト」の報告なのでまだ親切です。問題はその先で、行の続きが l.3 ^^93^^fa^^96{^^8c^^ea^^82̕\^^8e と表示される——本 の後半バイトが { として、表 の後半バイトが \ として、そのまま TeX の構文になっている のが見えます。だから症状は「日本語が化ける」ではなく「見覚えのないコマンドが未定義だと言われる」になり、原因がエンコーディングだと気づきにくいのです。
$ 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).この事故は Shift_JIS の設計から必然的に出てきます。2 バイト文字の 後半バイトが ASCII の範囲(0x40〜0x7E)に食い込む 方式なので、後半バイトがちょうど 0x5C(\)になる文字が 52 字、ちょうど 0x7B({)になる文字が 50 字 あります。前者には 表・十・ソ・能・貼・暴・申・構 が、後者には 本・宮・施・旬・養・鶏 が含まれます——どれも普通の日本語に頻出する字です。日本語の開発者がこれを「ダメ文字」と呼んで長く苦しんだのは、TeX に限らず、シェルスクリプトでも設定ファイルでも同じ事故が起きたからです。EUC-JP はこの問題を持たない(後半バイトが常に 0x80 以上)ので、TeX の現場では EUC-JP のほうが好まれた時期がありました。
Shift_JIS を UTF-8 に変換する — iconv と nkf
日本語圏の定番は nkf(Network Kanji Filter) ですが、これは追加インストールが要る道具で、macOS にも多くの Linux にも最初から入っていません。先に iconv を試してください——POSIX の標準ツールなので、macOS にも Linux にも /usr/bin/iconv として必ずあります。iconv -f CP932 -t UTF-8 old.tex > new.tex で Shift_JIS から UTF-8 への変換は済みます。nkf が入っているなら nkf -w -Lu --overwrite *.tex の一行で複数ファイルを直接書き換えられるので、大量のファイルを扱うときは nkf を入れる価値があります。どちらを使うにせよ、変換前に必ずコピーを取るか、Git に入れてから走らせること。--overwrite は文字どおり元のファイルを潰します。
# 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 *.texiconv を使うときは、エンコーディング名を SHIFT_JIS ではなく CP932 にしてください。この二つは同じものだと思われがちですが、実際には違います。macOS の iconv で ~(波ダッシュ)や ①(丸数字)を含むテキストを SHIFT_JIS に変換しようとすると iconv: iconv(): Illegal byte sequence で止まり、CP932 なら通ります。SHIFT_JIS は JIS X 0208 に忠実な狭い定義で、丸数字やローマ数字などの NEC・IBM 拡張文字を含まない からです。Windows 由来のファイルは事実上すべて CP932 なので、既定で CP932 を指定しておくのが正解です。逆向き(UTF-8 → Shift_JIS)に変換して古いツールに渡す必要があるときも、同じ理由で CP932 を選びます。
| nkf のオプション | 働き | iconv での相当 |
|---|---|---|
-g | 現在のエンコーディングと改行を判定する(変換はしない) | 相当なし。file や chardetect を使う |
-w | UTF-8(BOM なし)に変換する | iconv -t UTF-8 |
-s / -e / -j | Shift_JIS / EUC-JP / ISO-2022-JP に変換する | iconv -t CP932 / -t EUC-JP / -t ISO-2022-JP |
-Lu / -Lw / -Lm | 改行を LF / CRLF / CR に揃える | 相当なし。sed、dos2unix、Git の eol=lf を使う |
--overwrite | 引数のファイルを直接書き換える | 相当なし。iconv は標準出力に書くので、必ず別ファイルへ |
-kanji= — 変換せずにエンジンへ読み方を教える
ファイルを書き換えたくない、あるいは書き換える権限がないときは、エンジン側にエンコーディングを教えます。(u)platex は -kanji= を受け付け、-kanji=sjis・-kanji=euc・-kanji=jis・-kanji=utf8 を指定できます。実際、UTF-8 として読めば大量のエラーを吐いた Shift_JIS のファイルが、uplatex -kanji=sjis sjis.tex では Output written on sjis.dvi (1 page, 320 bytes). で通りました。ただしこれは 応急処置 です。エディタ・Git・grep・\input で読み込む別ファイルの側は依然として UTF-8 だと思って動くので、混在した瞬間に別の事故が起きます。「他人から受け取った原稿をとりあえず一度組んで中身を確かめる」までの手段と考え、確認できたら変換してしまうのが正解です。なお lualatex と xelatex に -kanji= はありません——これらは常に UTF-8 を読みます。
platex と uplatex の違い — 同じバイナリ、違う文字の世界
違いは扱える文字の範囲であって、プログラムそのものではありません。TeX Live 2024 で platex --version と uplatex --version を並べると、どちらも e-upTeX 3.141592653-p4.1.1-u1.30-230214-2.6 と名乗ります——同じバイナリです。差は括弧の中だけで、platex は (utf8.euc)、uplatex は (utf8.uptex)。読み込むフォーマットが違うので、内部の文字コードの持ち方が変わります。その帰結は具体的です。人名によく使う 髙(はしごだか、U+9AD9)を書いて platex に通すと ! LaTeX Error: Unicode character ^^e9^^ab^^99 (U+9AD9) で止まり、uplatex は何も言わずに組み上げます。無印 platex の内部は JIS X 0208 の範囲に閉じていて、そこに無い文字は入口で弾かれるからです。新しく書く文書で platex を選ぶ理由はもうありません。 uplatex を既定にしておけば、髙 も 𠮟 も名簿の異体字も通ります。
$ 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).改行コードと BOM — LF・CRLF・CR は事故になるか
LaTeX 側はどれも黙って受け付けます。 \r\n(CRLF、Windows)で書かれた .tex を uplatex に渡しても、UTF-8 の先頭に BOM(EF BB BF)が付いていても、TeX Live 2024 の uplatex も lualatex も何の警告も出さずに通します。BOM が入っていると先頭に見えない文字が出る、という話をよく聞きますが、少なくとも現在のこの二つのエンジンではそうなりません。困るのは LaTeX ではなく 周りのツール です。CRLF と LF が混ざったファイルは Git の差分が全行変更に見え、レビューが不可能になります。行末の \r が残った .sty を grep すると、行末アンカーが効かないことがあります。だから LF に統一する のは組版のためではなく、共同作業のためです。Git を使っているなら .gitattributes に一行足すのがもっとも確実で、チェックアウト時の環境差も吸収してくれます。
# .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- 新規も既存も UTF-8 + LF に統一する。 TeX Live 2024 の三エンジンすべての既定であり、Git とも噛み合う。
- 変換は
iconv -f CP932 -t UTF-8から。SHIFT_JISではなくCP932を指定する(丸数字・波ダッシュが通らなくなる)。 nkfがあるならnkf -w -Lu --overwrite *.texが一番速い。 ただし macOS にも多くの Linux にも標準では入っていない。- 変換の前にコピーか Git のコミットを。
--overwriteは元に戻せない。 - 新規文書は
uplatex(またはlualatex)で。 無印platexは髙(U+9AD9)のような JIS X 0208 外の字でエラーになる。 -kanji=sjisは応急処置。 一度中身を確認したら、ファイル自体を UTF-8 に変換する。