1. エラーの症状
理想の世界では、プログラマーは常にファイルのエンコーディングを把握し、読み込み時に正しく指定します。しかし現実は、ファイルが Windows、Linux、サーバ、エディタの間を行き来し、それぞれが独自にバイトを解釈する世界です。その結果、次のような症状に直面します。
- 「文字化け」 — 期待したテキストの代わりに、見慣れない記号、クエスチョンマーク、四角、どの言語にも見えない文字列が表示される。
- 文字の欠落 — テキストの一部が消える、または ? に置き換わる。
- 例外 — 例えば MalformedInputException。選択したエンコーディングとしてバイトを「消化」できないときに発生します。
- パース時のエラー — テキストの歪みによりキーワードや構造が壊れ、プログラムがファイルを正しく処理できない。
誤ったエンコーディングでキリル文字のファイルを読むときの「文字化け」の典型例:
期待: こんにちは、世界!
実際: Привет, мир
これは新しい言語ではなく、本来必要な「辞書」(エンコーディング)とは別の規則でバイトが解釈された結果です。
2. なぜエラーが起きるのか: 根本原因
保存時と読み込み時のエンコーディングが違う
例えば誰かがファイルを Windows-1251 で保存し、あなたがそれを UTF-8 で開いたとします。Java は UTF-8 の規則でバイトを解釈しようとしますが、バイト値が期待と一致しないため、意味不明な結果になります。
システムのデフォルトエンコーディングに依存している
エンコーディングを明示しないと、Java はシステムのデフォルト(あなたのマシンに設定されたもの)を使います。Windows のロケールがロシア語なら Windows-1251、Linux は UTF-8、Mac も UTF-8 です。あなたの環境で問題なく開けるファイルが、別の OS を使う同僚の環境では読めなくなることがあります。
古いコンストラクタの使用
古い Java(や一部の教材)では、FileReader/FileWriter のようにシステムのエンコーディングを使い、制御できない書き方がよく見られます。これは罠であり、「文字化け」の温床です。
FileReader reader = new FileReader("file.txt");
FileWriter writer = new FileWriter("file.txt");
BOM (Byte Order Mark) の有無
一部のエンコーディング(例えば UTF-8 の BOM 付きや UTF-16)は、先頭に特別なバイトを付与して自分の種類を示します。プログラムが BOM を想定していない、または逆に期待しているのに無い場合、問題が起きることがあります。先頭の文字が壊れたり、ファイルがまったく認識されなかったりします。
3. エラーの現れ方: 実例で確認
例1: キリル文字のファイルが Windows-1251 で保存されているのに、UTF-8 として読む
import java.nio.file.*;
import java.nio.charset.*;
public class EncodingDemo {
public static void main(String[] args) throws Exception {
Path path = Paths.get("russian.txt");
// ファイルは Windows-1251 で保存されているのに、UTF-8 として読む — 文字化けする!
try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
System.out.println(reader.readLine());
}
}
}
結果として「こんにちは、世界!」の代わりに奇妙な文字が並んで表示されます。
例2: ファイルは UTF-8、読み込みは ISO-8859-1
try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.ISO_8859_1)) {
System.out.println(reader.readLine());
}
結果: ASCII 以外の文字はゴミになるか、? に置き換えられます。
例3: ファイル読み込み時の例外
バイト列が選択したエンコーディングの規則に合わないと、Java は例外を投げることがあります:
Exception in thread "main" java.nio.charset.MalformedInputException: Input length = 1
at java.base/sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
...
これは、選択したエンコーディングでは正しく解釈できないバイトを Java が検出したことを意味します。
4. 診断: エンコーディングの問題を見抜くには
ファイルのエンコーディングを確認する
- Notepad++、VS Code、Sublime Text などのエディタでは、通常ファイルのエンコーディングを確認・変更できます(多くは下部のステータスバーに表示)。
- Linux ではコマンドで推測できます(ただし常に 100% 正確とは限りません):
file file_name.txt
Java のシステムエンコーディングを確認する
プロパティ file.encoding の値をコンソールに出力します:
System.out.println(System.getProperty("file.encoding"));
テスト用データを使う
キリル文字、ラテン文字、記号、絵文字など様々な文字を含む小さなファイルを作成し、異なるエンコーディングで読み込んで、結果が期待と一致するか確認します。
常にエンコーディングを明示する
エンコーディングを指定せずにファイルを読み書きしているコードを見かけたら要注意です。例えば Files.newBufferedReader(..., StandardCharsets.UTF_8) を使い、「デフォルト」頼みにはしないでください。
5. ベストプラクティス: 落とし穴を避けるには
ルール1:
必ず ファイル入出力ではエンコーディングを明示してください。特に、異なるコンピュータや OS 間で利用したり、ネットワーク経由で送受信する場合は重要です。
ルール2:
現代的で意味の明確なエンコーディング、特に UTF-8(StandardCharsets.UTF_8)を使用しましょう。レガシーシステムとの連携など特別な要件がある場合にのみ、他のエンコーディングを使ってください。
ルール3:
FileReader や FileWriter(エンコーディングを指定できない)は避け、代わりに InputStreamReader、OutputStreamWriter、または Files のメソッドを Charset を明示して使いましょう。
ルール4:
結果を必ず確認しましょう。書き出したファイルを複数のエンコーディングを切り替えられるエディタで開き、テキストが正しく表示されるか検証してください。
6. 注意点と細かな話: BOM、XML、JSON などの厄介なケース
BOM (Byte Order Mark): UTF-8 のファイルが「見えない」バイト(EF BB BF)で始まることがあります。多くの現代的なプログラムはこれを無視しますが、一部は先頭に「文字化け」を表示したり、ファイルを受け付けなかったりします(例えば古い XML/JSON パーサ)。
XML/HTML: 冒頭に <?xml version="1.0" encoding="UTF-8"?> のような宣言がある場合があります。これはプログラムに「どのエンコーディングを期待すべきか」を伝えます。しかし実際のエンコーディングが宣言と一致しないと、またしても「文字化け」です。
JSON: 標準では UTF-8 であるべきですが、もしファイルが Windows-1251 で作られていると、パーサはエラーを出すか、データが歪みます。
GO TO FULL VERSION