0

utf-16LE でエンコードされたファイルからコンテンツを取得するファイル ウォッチャーがあります。書き込まれたデータの最初のビットには、利用可能な BOM があります。これを使用して、UTF-8 に対するエンコーディングを識別していました (受信するほとんどのファイルがエンコーディングされています)。BOM をキャッチして UTF-8 に再エンコードし、パーサーが異常を起こさないようにします。問題は、ファイルが拡大するため、データのすべてのビットに BOM が含まれているわけではないことです。

これが私の質問です - 私が持っているデータの各セットに BOM バイトを追加せずに (ソースを制御できないため)、UTF-16 \000 に固有の null バイトを探してから使用できますか? BOMの代わりに私の識別子としてそれを?これにより、今後頭痛がすることはありますか?

私のアーキテクチャには、Java で記述されたパーサーが取得したときに受信したデータを一時ファイルに記録する ruby​​ Web アプリケーションが含まれます。

私の識別/再エンコードコードは次のようになります。

  // guess encoding if utf-16 then
  // convert to UTF-8 first
  try {
    FileInputStream fis = new FileInputStream(args[args.length-1]);
    byte[] contents = new byte[fis.available()];
    fis.read(contents, 0, contents.length);

    if ( (contents[0] == (byte)0xFF) && (contents[1] == (byte)0xFE) ) {
      String asString = new String(contents, "UTF-16");
      byte[] newBytes = asString.getBytes("UTF8");
      FileOutputStream fos = new FileOutputStream(args[args.length-1]);
      fos.write(newBytes);
      fos.close();
    }

    fis.close();
    } catch(Exception e) {
      e.printStackTrace();
  }

アップデート

ユーロ、em ダッシュ、およびその他の文字などをサポートしたいと考えています。上記のコードを次のように変更したところ、これらの文字のすべてのテストに合格したようです。

  // guess encoding if utf-16 then
  // convert to UTF-8 first
  try {
    FileInputStream fis = new FileInputStream(args[args.length-1]);
    byte[] contents = new byte[fis.available()];
    fis.read(contents, 0, contents.length);
    byte[] real = null;

    int found = 0;

    // if found a BOM then skip out of here... we just need to convert it
    if ( (contents[0] == (byte)0xFF) && (contents[1] == (byte)0xFE) ) {
      found = 3;
      real = contents;

    // no BOM detected but still could be UTF-16
    } else {

      for(int cnt=0; cnt<10; cnt++) {
        if(contents[cnt] == (byte)0x00) { found++; };

        real = new byte[contents.length+2];
        real[0] = (byte)0xFF;
        real[1] = (byte)0xFE;

        // tack on BOM and copy over new array
        for(int ib=2; ib < real.length; ib++) {
          real[ib] = contents[ib-2];
        }
      }

    }

    if(found >= 2) {
      String asString = new String(real, "UTF-16");
      byte[] newBytes = asString.getBytes("UTF8");
      FileOutputStream fos = new FileOutputStream(args[args.length-1]);
      fos.write(newBytes);
      fos.close();
    }

    fis.close();
    } catch(Exception e) {
      e.printStackTrace();
  }

皆さんはどう思いますか?

4

3 に答える 3

6

一般に、データ ストリームの文字エンコーディングを 100% の精度で識別することはできません。あなたができる最善の方法は、予想されるエンコーディングの限られたセットを使用してデコードを試み、デコードされた結果にヒューリスティックを適用して、予想される言語のテキストに「似ている」かどうかを確認することです。(しかし、どのヒューリスティックも、特定のデータ ストリームに対して偽陽性と偽陰性を示します。) または、人間をループに入れて、どのデコードが最も理にかなっているかを判断します。

より良い解決策は、データを提供しているものは何でも、データに使用されるエンコード方式も提供する必要があるように、プロトコルを再設計することです。(それができない場合は、エンコーディング スキームを提供できないシステムの設計/実装の責任者を非難してください!)。

編集: 質問に対するコメントから、データ ファイルは HTTP 経由で配信されています。この場合、HTTP サーバーがデータを配信する POST 要求の「コンテンツ タイプ」ヘッダーをスナーフし、ヘッダーから文字セット/エンコーディングを抽出し、ファイル パーサーが可能な方法/場所に保存するように調整する必要があります。対処する。

于 2009-08-28T00:50:13.123 に答える
0

これは間違いなく、将来的に頭痛の種になるでしょう。単純なケース (ASCII のみ、UTF-16、いずれかのバイト順) では交互のゼロバイトをチェックできますが、0x7f コードポイントを超える文字のストリームを取得し始めると、その方法は役に立たなくなります。

ファイル ハンドルがある場合、最善の策は、現在のファイル ポインターを保存し、最初にシークし、BOM を読み取ってから、元の位置にシークすることです。

それか、何とかBOMを覚えておいてください。

すべての入力に対して文字範囲が制限されることが絶対に確実でない限り、データの内容に依存することはお勧めできません。

于 2009-08-28T00:50:56.947 に答える
0

この質問には、BOM を必要としない文字検出のオプションがいくつか含まれています。

私のプロジェクトは現在jCharDetを使用し ていますが、jCharDet は 100% 信頼できるわけではないため、そこにリストされている他のオプションを調べる必要があるかもしれません。

于 2009-08-28T05:15:07.420 に答える