CodeGym /コース /JAVA 25 SELF /エンコーディング不一致の問題と典型的なミス

エンコーディング不一致の問題と典型的なミス

JAVA 25 SELF
レベル 37 , レッスン 3
使用可能

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-8BOM 付きや 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-8StandardCharsets.UTF_8)を使用しましょう。レガシーシステムとの連携など特別な要件がある場合にのみ、他のエンコーディングを使ってください。

ルール3:
FileReaderFileWriter(エンコーディングを指定できない)は避け、代わりに InputStreamReaderOutputStreamWriter、または 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 で作られていると、パーサはエラーを出すか、データが歪みます。

1
タスク
JAVA 25 SELF, レベル 37, レッスン 3
ロック未解除
誤ったレンズで古代の巻物を解読しようとする試み 🔎
誤ったレンズで古代の巻物を解読しようとする試み 🔎
1
タスク
JAVA 25 SELF, レベル 37, レッスン 3
ロック未解除
「翻訳者」の比較分析:テキストの意味はどのように変わるか 🌍
「翻訳者」の比較分析:テキストの意味はどのように変わるか 🌍
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION