如何在不使用BOM的情况下识别不同的编码?

时间:2009-08-28 00:31:57

标签: java utf-8 utf-16 byte-order-mark

我有一个文件观察程序正在从使用utf-16LE编码的不断增长的文件中获取内容。写入它的第一位数据有BOM可用 - 我用它来识别UTF-8的编码(我的文件的MOST编码在其中)。我抓住了BOM并重新编码为UTF-8,所以我的解析器并没有吓坏。问题是,由于它是一个不断增长的文件,并不是每一位数据都有BOM。

这是我的问题 - 没有将BOM字节预先添加到我拥有的每组数据(因为我没有对源的控制)我可以只查找固有的空字节在UTF-16 \ 000中,然后使用它作为我的标识符而不是BOM?这会让我头疼吗?

我的架构涉及一个ruby Web应用程序,当我用java编写的解析器选中它时,将收到的数据记录到一个临时文件中。

现在写我的识别/重新编码代码如下:

  // 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-dashes和其他角色之类的东西。 我修改了上面的代码看起来像这样,它似乎通过了我所有的测试 对于那些角色:

  // 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();
  }

你们都在想什么?

3 个答案:

答案 0 :(得分:6)

通常,您无法100%准确地识别数据流的字符编码。您可以做的最好的事情是尝试使用一组有限的预期编码进行解码,然后对解码结果应用一些启发式方法,以查看它是否看起来像预期语言中的文本。 (但任何启发式方法都会给某些数据流带来误报和漏报。)或者,将一个人放入循环中以决定哪种解码最有意义。

更好的解决方案是重新设计协议,以便提供数据的任何内容都必须提供用于数据的编码方案。 (如果你不能,责怪谁负责设计/实现无法给你编码方案的系统!)。

编辑:根据您对该问题的评论,数据文件通过HTTP传递。在这种情况下,您应该安排您的HTTP服务器侦听传递数据的POST请求的“content-type”标头,从标头中提取字符集/编码,并将其保存在文件解析器可以的方式/位置处理。

答案 1 :(得分:0)

这将使你头疼,毫无疑问。您可以检查简单情况的交替零字节(仅ASCII,UTF-16,字节顺序),但是当您开始获得0x7f代码点之上的字符流时,该方法就变得无用了。

如果你有文件句柄,最好的办法是保存当前文件指针,寻找开始,读取BOM然后寻找原来的位置。

或者以某种方式记住BOM。

依赖于数据内容是一个错误的想法,除非您完全确定所有输入都将限制字符范围。

答案 2 :(得分:0)

This question包含一些字符检测选项,似乎不需要BOM。

我的项目目前正在使用jCharDet但我可能需要查看其中列出的其他一些选项,因为jCharDet不是100%可靠。