1. 概要

不正な文字のコンパイルエラーは、ファイルタイプのエンコードエラーです。 ファイルの作成時にファイルで誤ったエンコーディングを使用した場合に生成されます。 その結果、Javaなどの言語では、プロジェクトをコンパイルしようとすると、このタイプのエラーが発生する可能性があります。 このチュートリアルでは、問題が発生する可能性のあるいくつかのシナリオとともに問題を詳細に説明し、次に、問題を解決する方法の例をいくつか示します。

2. 不正な文字のコンパイルエラー

2.1. バイトオーダーマーク(BOM)

バイト順マークに入る前に、 UCS(Unicode)変換形式(UTF)。 UTFは、Unicodeで可能なすべての文字コードポイントをエンコードできる文字エンコード形式です。 UTFエンコーディングにはいくつかの種類があります。 これらすべての中で、UTF-8が最も使用されています。

UTF-8は、ASCIIとの互換性を最大化するために、8ビットの可変幅エンコーディングを使用します。 このエンコーディングをファイルで使用すると、Unicodeコードポイントを表すバイトが見つかる場合があります。 その結果、ファイルはU + FEFFバイトオーダーマーク(BOM)で始まります。 正しく使用されているこのマークは見えません。 ただし、場合によっては、データエラーが発生する可能性があります。

UTF-8エンコーディングでは、BOMの存在は基本的ではありません は必須ではありませんが、BOMはUTF-8でエンコードされたテキストで表示される場合があります。 BOMの追加は、エンコーディング変換によって、またはコンテンツにUTF-8としてフラグを立てるテキストエディタによって発生する可能性があります。

Windowsのメモ帳のようなテキストエディタは、この種の追加を生成する可能性があります。 結果として、メモ帳のようなテキストエディタを使用してコード例を作成し、それを実行しようとすると、コンパイルエラーが発生する可能性があります。 対照的に、最新のIDEは、作成されたファイルをBOMなしのUTF-8としてエンコードします。 次のセクションでは、この問題のいくつかの例を示します。

2.2. 不正な文字のコンパイルエラーのあるクラス

通常、高度なIDEを使用しますが、代わりにテキストエディタを使用する場合もあります。 残念ながら、私たちが学んだように、BOMを使用してファイルを保存すると、Javaでコンパイルエラーが発生する可能性があるため、一部のテキストエディタは解決策よりも多くの問題を引き起こす可能性があります。「不正な文字」エラーはコンパイルフェーズで発生します。したがって、検出は非常に簡単です。 次の例は、それがどのように機能するかを示しています。

まず、メモ帳などの簡単なクラスをテキストエディタで作成しましょう。 このクラスは単なる表現であり、テストする任意のコードを記述できます。 次に、テストするBOMを使用してファイルを保存します。

public class TestBOM {
    public static void main(String ...args){
        System.out.println("BOM Test");
    }
}

ここで、 javac コマンドを使用してこのファイルをコンパイルしようとすると、次のようになります。

$ javac ./TestBOM.java

その結果、次のエラーメッセージが表示されます。

public class TestBOM {
 ^
.\TestBOM.java:1: error: illegal character: '\u00bf'
public class TestBOM {
  ^
2 errors

理想的には、この問題を修正するには、ファイルをBOMエンコードなしでUTF-8として保存するだけです。 その後、問題は解決します。 ファイルがBOMなしで保存されていることを常に確認する必要があります

この問題を修正する別の方法は、dos2unixなどのツールを使用することです。 このツールはBOMを削除し、Windowsテキストファイルの他の特異性も処理します。

3. ファイルの読み取り

さらに、BOMでエンコードされたファイルの読み取りの例をいくつか分析してみましょう。

最初に、テストに使用するBOMを使用してファイルを作成する必要があります。 このファイルには、サンプルテキスト「HelloworldwithBOM」が含まれています。 –これが予想される文字列になります。 次に、テストを開始しましょう。

3.1. BufferedReaderを使用したファイルの読み取り

まず、BufferedReaderクラスを使用してファイルをテストします。

@Test
public void whenInputFileHasBOM_thenUseInputStream() throws IOException {
    String line;
    String actual = "";
    try (BufferedReader br = new BufferedReader(new InputStreamReader(file))) {
        while ((line = br.readLine()) != null) {
            actual += line;
        }
    }
    assertEquals(expected, actual);
}

この場合、文字列が等しいと断言しようとすると、エラーが発生します

org.opentest4j.AssertionFailedError: expected: <Hello world with BOM.> but was: <Hello world with BOM.>
Expected :Hello world with BOM.
Actual   :Hello world with BOM.

実際、テスト応答をざっと見ると、両方の文字列は明らかに同じように見えます。 それでも、文字列の実際の値にはBOMが含まれています。 結果として、文字列は等しくありません。

さらに、簡単な修正はBOM文字を置き換えることです

@Test
public void whenInputFileHasBOM_thenUseInputStreamWithReplace() throws IOException {
    String line;
    String actual = "";
    try (BufferedReader br = new BufferedReader(new InputStreamReader(file))) {
        while ((line = br.readLine()) != null) {
            actual += line.replace("\uFEFF", "");
        }
    }
    assertEquals(expected, actual);
}

replace メソッドは、文字列からBOMをクリアするため、テストに合格します。 replaceメソッドを慎重に使用する必要があります。 処理するファイルの数が膨大になると、パフォーマンスの問題が発生する可能性があります。

3.2. ApacheCommonsIOを使用したファイルの読み取り

さらに、 ApacheCommonsIOライブラリはBOMInputStreamクラスを提供します。 このクラスは、エンコードされたByteOrderMarkを最初のバイトとして含むラッパーです。 それがどのように機能するか見てみましょう:

@Test
public void whenInputFileHasBOM_thenUseBOMInputStream() throws IOException {
    String line;
    String actual = "";
    ByteOrderMark[] byteOrderMarks = new ByteOrderMark[] { 
      ByteOrderMark.UTF_8, ByteOrderMark.UTF_16BE, ByteOrderMark.UTF_16LE, ByteOrderMark.UTF_32BE, ByteOrderMark.UTF_32LE
    };
    InputStream inputStream = new BOMInputStream(ioStream, false, byteOrderMarks);
    Reader reader = new InputStreamReader(inputStream);
    BufferedReader br = new BufferedReader(reader);
    while ((line = br.readLine()) != null) {
        actual += line;
    }
    assertEquals(expected, actual);
}

コードは前の例と似ていますが、BOMInputStreamをパラメーターとしてInputStreamReaderに渡します。

3.3. Google Data(GData)を使用したファイルの読み取り

一方、 BOMを処理するためのもう1つの便利なライブラリは、Google Data(GData)です。 これは古いライブラリですが、ファイル内のBOMの管理に役立ちます。 基礎となる形式としてXMLを使用します。 実際の動作を見てみましょう。

@Test
public void whenInputFileHasBOM_thenUseGoogleGdata() throws IOException {
    char[] actual = new char[21];
    try (Reader r = new UnicodeReader(ioStream, null)) {
        r.read(actual);
    }
    assertEquals(expected, String.valueOf(actual));
}

最後に、前の例で観察したように、ファイルからBOMを削除することが重要です。 ファイルで適切に処理しないと、データの読み取り時に予期しない結果が発生します。 そのため、ファイルにこのマークが存在することに注意する必要があります。

4. 結論

この記事では、Javaでの不正な文字のコンパイルエラーに関するいくつかのトピックについて説明しました。 まず、UTFとは何か、BOMがどのように統合されているかを学びました。 次に、テキストエディタ(この場合はWindowsのメモ帳)を使用して作成されたサンプルクラスを示しました。 生成されたクラスは、不正な文字のコンパイルエラーをスローしました。 最後に、BOMを使用してファイルを読み取る方法に関するいくつかのコード例を示しました。

いつものように、この例で使用されているすべてのコードは、GitHubにあります。