はじめに
「文字コードを変えるだけでしょ?」──そう言って痛い目を見たエンジニアは少なくない。
日本語の文字コード問題は、英語圏のそれとはレベルが違う。UTF-8 に変換したはずなのに特定の文字だけ ? になる。ソートをかけたら順番がおかしい。「表」という字を含むファイルパスが Windows で壊れる。何十年もシステムを動かしてきた現場で、突然こういった問題が噴出する。
メインフレームのモダナイゼーションでは、EBCDIC・Shift-JIS・UTF-8 が混在する日本語文字コードの課題が頻繁に発生する。本記事では、なぜ日本語の文字コード問題はこれほど複雑なのか、その構造的な理由を整理する。
実務での変換手順や COBOL/MFES に特化した内容は、後続の記事「汎用機マイグレーションにおける日本語処理 完全ガイド」で扱う。本記事はその前提知識として位置づけてほしい。
1. 「文字コード」は1種類ではない──日本語が特殊な理由
英語圏では基本的に ASCII(128文字)で事足りる。大文字・小文字・数字・記号を合わせても 128文字以内に収まるからだ。
日本語はそうはいかない。ひらがな・カタカナ・漢字(JIS第一〜第四水準だけで約2万字)を扱うには、8ビット(1バイト)では到底足りない。そこで生まれたのが「多バイト文字コード」だが、これを各コンピュータメーカーがそれぞれ独自に実装したことで、現在の混乱が生まれた。
日本語文字コードの系譜
| 時代 | 文字コード | 誰が作ったか |
|---|---|---|
| 1960年代 | IBM EBCDIC + 独自拡張 | IBM(汎用機) |
| 1970年代 | JIS C 6226(区点コード) | 日本規格協会 |
| 1983年 | Shift-JIS | Microsoft・ASCII(後にJIS化) |
| 1990年代 | CP932(Windows-31J) | Microsoft が独自拡張 |
| 2000年代〜 | UTF-8 / UTF-16 | Unicode Consortium |
これだけ多くの「別物」が生まれた背景には、コンピュータ黎明期に標準化が追いつかなかった歴史的事情がある。そして厄介なことに、これらは今もなお現役のシステムの中で混在している。
2. EBCDIC──汎用機が作り出した「乱立」の温床
EBCDIC(Extended Binary Coded Decimal Interchange Code)は 1964 年に IBM が発表した文字コードで、メインフレームの標準として採用された。問題は、日本語対応をそれぞれのメーカーが独自に拡張したことだ。
| メーカー | 独自コード | 正式名称 |
|---|---|---|
| IBM | IBM漢字 | IBM-930 / 939 |
| 富士通 | JEF漢字 | — |
| 日立製作所 | EBCDIK | — |
| NEC | NEC漢字 | — |
これらは互いに非互換だ。「EBCDIC から変換する」と一口に言っても、どのメーカーの EBCDIC かによって変換テーブルが全く違う。富士通の JEF で書かれたファイルを IBM の変換ロジックで処理すると、文字が化けるどころか全く別の漢字に変わることがある。
EBCDIC の構造的な特徴
IBM 日本語 EBCDIC(IBM-930/939)の技術的な特徴を押さえておきたい。
1バイト文字と2バイト文字の混在
日本語 EBCDIC では、英数字は 1 バイト、漢字は 2 バイトで表現する。しかしそのままでは「この 2 バイトは 1 バイト+1 バイトか、それとも 2 バイト文字か」が区別できない。
そこで使われるのがシフトコードだ。
[1バイト文字] [0x0E (SO:シフトアウト)] [2バイト漢字] [0x0F (SI:シフトイン)] [1バイト文字]
0x0E(Shift-Out)で「ここから2バイト文字モード」、0x0F(Shift-In)で「ここから1バイト文字モードに戻る」と宣言する。このシフトコードがデータの中に混入しているため、EBCDIC のバイト列はそのままでは意味を正しく読み取れない。
外字(ユーザー定義文字)の問題
IBM EBCDIC 漢字は最大 4,370 字 の外字(ユーザーが独自に登録した文字)を持てる。これが変換先によって大きく制限される。
| 変換先 | 外字サポート数 |
|---|---|
| Shift-JIS | 最大 1,880 字 |
| Unicode(UTF-8/UTF-16) | 最大 4,370 字 |
Shift-JIS に変換すると、最大 2,490 字が収容不可能になる。長年使い続けてきた外字が、変換した瞬間に消える。これが「EBCDIC から Shift-JIS に変換すれば終わり」と思っていたプロジェクトが詰まる定番の罠だ。
3. Shift-JIS(CP932)──設計上の欠陥を抱えたまま普及した
Shift-JIS は 1983 年に登場し、日本の PC・Windows の標準として 30 年以上使われてきた。現在も日本の業務システムの多くがこの文字コードを使っている。
しかし Shift-JIS には、設計当初から構造的な欠陥が埋め込まれていた。
Shift-JIS と CP932(Windows-31J)は別物
「Shift-JIS を使っています」と言うシステムの多くは、実際には CP932(Windows-31J) を使っている。Microsoft が Windows 向けに Shift-JIS を独自拡張したもので、正式な Shift-JIS と以下の点が異なる。
| 文字 | 正式 Shift-JIS | CP932(MS932) |
|---|---|---|
| ~(波ダッシュ) | 0x8160 | U+FF5E |
| -(全角マイナス) | 0x817C | U+FF0D |
| 髙(はしごだか) | 未定義 | 独自コード |
| 﨑(たつさき) | 未定義 | 独自コード |
| ①②③(丸数字) | 未定義 | 独自コード |
| ㈱(組み文字) | 未定義 | 独自コード |
「Shift-JIS と思っていたら実は CP932 だった」という事実は、Unicode へ変換する際に表面化する。同じ文字でもコードポイントが違うため、どちらの変換テーブルを使うかによって結果が変わる。
ダメ文字問題──Shift-JIS 最大の構造的欠陥
Shift-JIS の 2 バイト文字は、先頭バイトが 0x81〜0x9F または 0xE0〜0xFC、2 バイト目が 0x40〜0xFC の範囲を使う。
問題は2バイト目が ASCII 領域(0x00〜0x7F)と重なっている点だ。
特に 0x5C(バックスラッシュ / 円記号)との衝突が深刻で、以下の文字が影響を受ける。
| 文字 | Shift-JIS コード | 2バイト目 |
|---|---|---|
| ソ | 0x83 5C | 0x5C |
| 能 | 0x94 5C | 0x5C |
| 表 | 0x95 5C | 0x5C |
| 十 | 0x8F 5C | 0x5C |
C言語・Perl・シェルスクリプトは 0x5C をエスケープ文字として解釈する。「表」を含む文字列を処理すると、2バイト目の 0x5C が「次の文字をエスケープせよ」と解釈され、後続の 1 バイトが消費されてしまう。
"表" のバイト列: 0x95 0x5C
↓
C言語がこれを解釈すると:
0x95 は「不明な文字」として処理
0x5C は「\」(バックスラッシュ)として解釈
→ 次の 1 バイトが「エスケープされた文字」として食われる
↓
データが 1 バイトずれてレコード崩壊
COBOL プログラム自体はこの問題を直接受けにくいが、COBOL が出力したデータを Java・Python・シェル で処理した瞬間に破壊が起きる。これが「COBOL 側では問題ないのに、連携先で文字化けする」という謎現象の正体だ。
ソート順の問題
Shift-JIS の漢字のコードポイントは、JIS の区点コード順をベースにしている。一方 Unicode(UTF-8/UTF-16)の漢字はコードポイントが異なる。
つまり、Shift-JIS でソートした結果と UTF-8 でソートした結果は一致しない。
| 文字コード | ソート結果(アルファベット vs 数字) |
|---|---|
| EBCDIC | アルファベット → 数字 の順 |
| Shift-JIS / Unicode | 数字 → アルファベット の順 |
「EBCDIC から Shift-JIS に変換しただけ」でも、ソートを使っているバッチ処理の出力が変わる。これは業務ロジックの変更を意味する。
4. UTF-8──「標準」だが日本語文脈では落とし穴がある
UTF-8 は現在の事実上の世界標準であり、Web・API・Linux・クラウドすべての基盤になっている。構造的には Shift-JIS の欠陥を解決しており、多バイト文字の後続バイトは必ず 0x80 以上になるため ASCII との衝突が起きない。
ただし、Shift-JIS から UTF-8 に移行する際には日本語固有の落とし穴がある。
波ダッシュ問題
「〜」(波ダッシュ)は日本語で広く使われる記号だが、CP932 と JIS・Unicode では別のコードポイントが割り当てられている。
- JIS の波ダッシュ:U+301C(WAVE DASH)
- CP932 の波ダッシュ:U+FF5E(FULLWIDTH TILDE)
見た目は同じでも別文字として扱われるため、CP932 で保存されたデータを UTF-8 に変換するとき、どちらのコードポイントに変換するかによって後の比較処理・検索処理が変わる。
全角マイナスや縦線も同様の問題を抱えている。
逆変換できない文字
UTF-8(Unicode)の世界には CP932 に存在しない文字が含まれる。
| 文字 | Unicode | CP932 |
|---|---|---|
| 髙(はしごだか) | U+9AD9 | 独自コード(U+9AD9 と別) |
| 﨑(たつさき) | U+FA11 | 独自コード |
| 𠮷(つちよし) | U+20BB7 | 存在しない |
一度 UTF-8 に変換したデータを Shift-JIS(CP932)に戻そうとすると、対応するコードがない文字は変換できない。UTF-8 からの逆変換は完全ではないことを最初から設計に折り込む必要がある。
BOM(Byte Order Mark)の混入
UTF-8 はビット並び順が固定のため、本来 BOM は不要だ。しかし Excel や一部の Windows ツールが出力する UTF-8 ファイルには、先頭に 0xEF 0xBB 0xBF(BOM)が付く場合がある。
BOM なしを前提にしているプログラムがこのファイルを読むと、先頭に  という文字列が現れ、文字化けとして認識される。「UTF-8 に変換したのにファイルの先頭だけ化ける」という症状の多くはこれが原因だ。
5. 「同じ文字コード」でも別物になる──IBM の公式警告
IBM Japan は 2024 年に「モダナイゼーション 10 の罠:第 8 回 文字コード・属性の罠」と題した記事で、この問題を公式に警告している。IBM が明示している落とし穴を整理する。
① ソート順が変わる
EBCDIC・Shift-JIS・Unicode でソート順が異なる(前述の通り)。バッチ処理でソート結果を前提にした後続処理がある場合、文字コードを変えただけで業務結果が変わる。
② 外字が移行できない
IBM EBCDIC の外字 4,370 字のうち、Shift-JIS に変換できるのは 1,880 字だけ。残りは文字として存在できなくなる。
③ 波ダッシュ・全角マイナスのコードポイントずれ
「Shift-JIS を使っていた」つもりが実は CP932 で、Unicode への変換で波ダッシュや全角マイナスのコードポイントがずれる。
④ UTF-16 の BOM とバイトオーダー
UTF-16 を使う場合、ビッグエンディアン(BE)とリトルエンディアン(LE)の 2 種類があり、先頭の BOM でどちらかを識別する。BOM の扱いを誤ると、全文字が正しく読めなくなる。
IBM のコメントが示唆することは重要だ。文字コードの変更は「変換表を適用すれば終わり」ではなく、文字集合の違い・ソート順・外字・コードポイントのずれ・エンディアンといった複数の観点を同時に設計しなければならない。
6. 3 つの文字コードを対比する
| EBCDIC(IBM-930/939) | Shift-JIS(CP932) | UTF-8 | |
|---|---|---|---|
| 日本語バイト数 | 2バイト(+SOシフトコード) | 半角1B / 全角2B | 日本語は3バイト |
| ASCII衝突 | なし | あり(ダメ文字) | なし(設計で回避済み) |
| 外字収容数 | 最大4,370字 | 最大1,880字 | Unicode全域 |
| ソート順 | 区点コード順 | 区点コード順(異なる) | Unicodeコードポイント順 |
| 現代環境との親和性 | 低(汎用機専用) | 中(Windows系のみ) | 高(全プラットフォーム) |
| 設計上の欠陥 | シフトコード混入 | ダメ文字(0x5C衝突) | BOM混入リスク(軽微) |
おわりに
日本語の文字コード問題が複雑な理由は、3 つの力が絡み合っているからだ。
- 歴史的経緯:各社が独自に拡張したため非互換な実装が乱立した
- 構造的欠陥:Shift-JIS の設計は ASCII 衝突を回避できていない
- 普及の現実:欠陥があるまま 30 年以上使われてきたため、変えにくい
UTF-8 への移行は「正しい方向」だが、「変換すれば終わり」ではない。外字・波ダッシュ・ダメ文字・ソート順・BOM──それぞれの問題を個別に設計しなければ、変換後のシステムで新たな障害が起きる。
IBM が「要件定義段階で調査せよ」と警告するのは、これらの問題が設計後半で発覚するほど手戻りコストが爆発的に増大するからだ。
次の記事では、この前提知識をもとに Micro Focus Enterprise Server(MFES)と COBOL 固定長レコードを含む汎用機マイグレーションにおける具体的な変換設計と実装を扱う。