5

BOM のないファイルのエンコーディングを識別しようとすると、特にファイルが非 ASCII 文字で始まる場合に問題が発生しました。

ファイルのエンコーディングを識別する方法について、次の 2 つのトピックを見つけました。

現在、次のように、ファイルのさまざまなエンコーディング (UTF-8、UTF-16、UTF-32、UTF-16 no BOM など) を識別するクラスを作成しました。

public class UnicodeReader extends Reader {
private static final int BOM_SIZE = 4;
private final InputStreamReader reader;

/**
 * Construct UnicodeReader
 * @param in Input stream.
 * @param defaultEncoding Default encoding to be used if BOM is not found,
 * or <code>null</code> to use system default encoding.
 * @throws IOException If an I/O error occurs.
 */
public UnicodeReader(InputStream in, String defaultEncoding) throws IOException {
    byte bom[] = new byte[BOM_SIZE];
    String encoding;
    int unread;
    PushbackInputStream pushbackStream = new PushbackInputStream(in, BOM_SIZE);
    int n = pushbackStream.read(bom, 0, bom.length);

    // Read ahead four bytes and check for BOM marks.
    if ((bom[0] == (byte) 0xEF) && (bom[1] == (byte) 0xBB) && (bom[2] == (byte) 0xBF)) {
        encoding = "UTF-8";
        unread = n - 3;
    } else if ((bom[0] == (byte) 0xFE) && (bom[1] == (byte) 0xFF)) {
        encoding = "UTF-16BE";
        unread = n - 2;
    } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE)) {
        encoding = "UTF-16LE";
        unread = n - 2;
    } else if ((bom[0] == (byte) 0x00) && (bom[1] == (byte) 0x00) && (bom[2] == (byte) 0xFE) && (bom[3] == (byte) 0xFF)) {
        encoding = "UTF-32BE";
        unread = n - 4;
    } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE) && (bom[2] == (byte) 0x00) && (bom[3] == (byte) 0x00)) {
        encoding = "UTF-32LE";
        unread = n - 4;
    } else {
        // No BOM detected but still could be UTF-16
        int found = 0;
        for (int i = 0; i < 4; i++) {
            if (bom[i] == (byte) 0x00)
                found++;
        }

        if(found >= 2) {
            if(bom[0] == (byte) 0x00){
                encoding = "UTF-16BE";
            }
            else {
                encoding = "UTF-16LE";
            }
            unread = n;
        }
        else {
            encoding = defaultEncoding;
            unread = n;
        }
    }

    // Unread bytes if necessary and skip BOM marks.
    if (unread > 0) {
        pushbackStream.unread(bom, (n - unread), unread);
    } else if (unread < -1) {
        pushbackStream.unread(bom, 0, 0);
    }

    // Use given encoding.
    if (encoding == null) {
        reader = new InputStreamReader(pushbackStream);
    } else {
        reader = new InputStreamReader(pushbackStream, encoding);
    }
}

public String getEncoding() {
    return reader.getEncoding();
}

public int read(char[] cbuf, int off, int len) throws IOException {
    return reader.read(cbuf, off, len);
}

public void close() throws IOException {
    reader.close();
}

}

上記のコードは、ファイルが BOM なしで非 ASCII 文字で始まる場合を除いて、すべてのケースで適切に機能します。このような状況下では、ファイルがまだ BOM なしの UTF-16 であるかどうかをチェックするロジックが正しく機能せず、エンコーディングはデフォルトで UTF-8 として設定されます。

特にUTF-16 NO BOMファイルの場合、BOMなしでASCII以外の文字で始まるファイルのエンコーディングをチェックする方法がある場合は?

ありがとう、どんなアイデアでも大歓迎です。

4

3 に答える 3

1

一般的に言って、エンコーディングが提供されていない場合、エンコーディングを確実に知る方法はありません。

テキスト内の特定のパターン(上位ビットの設定、設定、設定、未設定、設定、設定、設定、未設定)によってUTF-8を推測できますが、それでも推測です。

UTF-16は難しいものです。同じストリームでBEとLEを正常に解析できます。どちらの方法でも、いくつかの文字が生成されます(ただし、意味のないテキストになる可能性があります)。

そこにあるいくつかのコードは、統計分析を使用して記号の頻度によってエンコードを推測しますが、テキスト(つまり「これはモンゴルのテキストです」)と頻度テーブル(テキストと一致しない場合があります)に関するいくつかの仮定が必要です。結局のところ、これは単なる推測であり、100%の場合には役に立ちません。

于 2011-04-14T02:01:30.830 に答える
1

最善のアプローチは、これを自分で実装しようとしないことです。代わりに、既存のライブラリを使用してこれを行います。Java:ストリームの正しい文字セットエンコーディングを決定する方法を参照してください。例えば:

実行できる最善の方法は、ファイルの最も可能性の高いエンコーディングを推測することであることに注意してください。一般的なケースでは、正しいエンコーディングを理解したことを100%確信することは不可能です。つまり、ファイルの作成時に使用されたエンコーディング。


これらのサードパーティライブラリも、私が遭遇したファイルのエンコーディングを識別できないと思います[...]要件を満たすために改善される可能性があります。

または、要件を満たすのが非常に難しいことを認識して、変更することもできます。例えば

  • 特定のエンコーディングセットに制限し、
  • ファイルを提供/アップロードする人がそのエンコーディング(または第一言語)が何であるかを正しく述べていること、および/または
  • システムが特定の割合でそれを間違えることを受け入れ、誰かが誤って述べられた/推測されたエンコーディングを修正できる手段を提供します。

事実に直面してください:これは理論的に解決できない問題です。

于 2011-04-14T02:01:52.703 に答える
0

有効なUnicodeストリームであることが確実な場合、BOMがない場合はUTF-8である必要があり(BOMは必須でも推奨でもないため)、BOMがある場合は、それが何であるかがわかります。

ランダムなエンコーディングである場合、確実に知る方法はありません。すべての場合に正しく推測することは不可能であるため、あなたが望むことができる最善のことは、時々間違っているだけです。

可能性を非常に小さなサブセットに制限できれば、推測が正しい可能性を高めることができます

唯一の信頼できる方法は、プロバイダーに何を提供しているかを伝えるように要求することです。完全な信頼性が必要な場合は、それが唯一の選択肢です。信頼性を必要としない場合は、推測しますが、間違っている場合もあります。

そもそも私たちの残りの部分がBOMの原因になることはめったにないので、私はあなたがWindowsの人でなければならないと感じています。私は定期的にtgagabytesのテキスト(Mac、Linux、Solaris、およびBSDシステム)を扱っており、その99%以上がUTF-8であり、BOMを含むテキストに出くわしたのは2回だけです。しかし、Windowsの人々はいつもそれで立ち往生していると聞きました。trueの場合、これにより選択が容易になる場合とそうでない場合があります。

于 2011-04-14T02:16:33.457 に答える