当前位置:  开发笔记 > 编程语言 > 正文

Java:如何确定流的正确charset编码

如何解决《Java:如何确定流的正确charset编码》经验,为你挑选了10个好方法。

参考以下线程: Java App:无法正确读取iso-8859-1编码文件

以编程方式确定输入流/文件的正确字符集编码的最佳方法是什么?

我尝试过使用以下内容:

File in =  new File(args[0]);
InputStreamReader r = new InputStreamReader(new FileInputStream(in));
System.out.println(r.getEncoding());

但是在我知道用ISO8859_1编码的文件中,上面的代码产生了ASCII,这是不正确的,并且不允许我正确地将文件的内容呈现回控制台.



1> Eduard Wirch..:

您无法确定任意字节流的编码.这是编码的本质.编码意味着字节值与其表示之间的映射.所以每个编码"都可能"是正确的.

的getEncoding()方法将返回其设置(读取的编码的JavaDoc),用于该流.它不会猜测你的编码.

一些流告诉您使用哪种编码来创建它们:XML,HTML.但不是任意字节流.

无论如何,如果必须,您可以尝试自己猜测编码.每种语言都有一个共同的频率.在英语中,char e经常出现,但ê似乎很少出现.在ISO-8859-1流中,通常没有0x00字符.但是UTF-16流有很多.

或者:你可以问用户.我已经看过应用程序以不同的编码为您呈现文件的片段,并要求您选择"正确"的文件.


那么我的编辑器,记事本++如何知道如何打开文件并向我显示正确的字符?
这并没有真正回答这个问题.操作应该使用http://docs.codehaus.org/display/GUESSENC/Home或http://icu-project.org/apiref/icu4j/com/ibm/icu/text/CharsetDetector.html或http: //jchardet.sourceforge.net/
@Eduard:"所以每个编码都可能"是正确的." 不太对劲.许多文本编码都有几个无效的模式,这些模式是文本_probably_而不是编码.实际上,给定文件的前两个字节,只有38%的组合是有效的UTF8.前5个代码点偶然有效UTF8的几率小于.77%.同样,UTF16BE和LE通常可以通过大量零字节和它们所在的位置轻松识别.
@Hamidam幸运的是它向你显示了正确的角色.当它猜错时(通常会),有一个选项(菜单>>编码)允许您更改编码.

2> Luciano Fian..:

我使用过这个库,类似于jchardet,用于检测Java中的编码:http: //code.google.com/p/juniversalchardet/


我发现这更准确:http://jchardet.sourceforge.net/(我正在测试ISO 8859-1编码的西欧语言文档,windows-1252,utf-8)

3> 小智..:

看看这个: http://site.icu-project.org/(icu4j)他们有用于检测来自IOStream的字符集的库可能很简单,如下所示:

BufferedInputStream bis = new BufferedInputStream(input);
CharsetDetector cd = new CharsetDetector();
cd.setText(bis);
CharsetMatch cm = cd.detect();

if (cm != null) {
   reader = cm.getReader();
   charset = cm.getName();
}else {
   throw new UnsupportedCharsetException()
}


我试过但它大大失败了:我在eclipse中制作了两个包含"öäüß"的文本文件.一个设置为iso编码,一个设置为utf8 - 两者都被检测为utf8!所以我尝试在我的高清(窗户)某处安装一个文件 - 这个被正确检测到("windows-1252").然后我在hd上创建了两个新文件,一个用编辑器编辑,另一个用notepad ++编辑.在两种情况下都检测到"Big5"(中文)!
编辑:好的我应该检查cm.getConfidence() - 用我的短"äöüß"信心是10.所以我必须决定什么是好的信心 - 但这绝对是可以接受的(charset检测)

4> Benny Neugeb..:

这是我的最爱:

TikaEncodingDetector

相关性:


  org.apache.any23
  apache-any23-encoding
  1.1

样品:

public static Charset guessCharset(InputStream is) throws IOException {
  return Charset.forName(new TikaEncodingDetector().guessEncoding(is));    
}

GuessEncoding

相关性:


  org.codehaus.guessencoding
  guessencoding
  1.4
  jar

样品:

  public static Charset guessCharset2(File file) throws IOException {
    return CharsetToolkit.guessEncoding(file, 4096, StandardCharsets.UTF_8);
  }


*Nota:***TikaEncodingDetector 1.1**实际上是**ICU4J 3.4**`CharsetDectector`类的薄包装器.

5> Zach Scriven..:

您当然可以通过使用a 解码并注意"malformed-input"或"unmappable-character"错误来验证特定字符集的文件.当然,这只会告诉你字符集是否错误; 它没有告诉你它是否正确.为此,您需要一个比较基础来评估解码结果,例如,您是否事先知道字符是否限于某个子集,或者文本是否遵循某种严格的格式?最重要的是,字符集检测是猜测而没有任何保证.CharsetDecoder



6> Stephan..:

使用哪个库?

在撰写本文时,它们出现了三个库:

GuessEncoding

ICU4J

juniversalchardet

我没有包含Apache Any23,因为它使用了ICU4j 3.4.

如何判断哪一个检测到了正确的字符集(或尽可能接近)?

不可能对上面每个库检测到的字符集进行认证.但是,可以依次询问他们并对返回的回复进行评分.

如何评分返回的回复?

每个响应可以分配一个点.响应越多,检测到的字符集就越有信心.这是一种简单的评分方法.你可以详细说明别人.

有没有示例代码?

以下是实现前几行中描述的策略的完整代码段.

public static String guessEncoding(InputStream input) throws IOException {
    // Load input data
    long count = 0;
    int n = 0, EOF = -1;
    byte[] buffer = new byte[4096];
    ByteArrayOutputStream output = new ByteArrayOutputStream();

    while ((EOF != (n = input.read(buffer))) && (count <= Integer.MAX_VALUE)) {
        output.write(buffer, 0, n);
        count += n;
    }

    if (count > Integer.MAX_VALUE) {
        throw new RuntimeException("Inputstream too large.");
    }

    byte[] data = output.toByteArray();

    // Detect encoding
    Map encodingsScores = new HashMap<>();

    // * GuessEncoding
    updateEncodingsScores(encodingsScores, new CharsetToolkit(data).guessEncoding().displayName());

    // * ICU4j
    CharsetDetector charsetDetector = new CharsetDetector();
    charsetDetector.setText(data);
    charsetDetector.enableInputFilter(true);
    CharsetMatch cm = charsetDetector.detect();
    if (cm != null) {
        updateEncodingsScores(encodingsScores, cm.getName());
    }

    // * juniversalchardset
    UniversalDetector universalDetector = new UniversalDetector(null);
    universalDetector.handleData(data, 0, data.length);
    universalDetector.dataEnd();
    String encodingName = universalDetector.getDetectedCharset();
    if (encodingName != null) {
        updateEncodingsScores(encodingsScores, encodingName);
    }

    // Find winning encoding
    Map.Entry maxEntry = null;
    for (Map.Entry e : encodingsScores.entrySet()) {
        if (maxEntry == null || (e.getValue()[0] > maxEntry.getValue()[0])) {
            maxEntry = e;
        }
    }

    String winningEncoding = maxEntry.getKey();
    //dumpEncodingsScores(encodingsScores);
    return winningEncoding;
}

private static void updateEncodingsScores(Map encodingsScores, String encoding) {
    String encodingName = encoding.toLowerCase();
    int[] encodingScore = encodingsScores.get(encodingName);

    if (encodingScore == null) {
        encodingsScores.put(encodingName, new int[] { 1 });
    } else {
        encodingScore[0]++;
    }
}    

private static void dumpEncodingsScores(Map encodingsScores) {
    System.out.println(toString(encodingsScores));
}

private static String toString(Map encodingsScores) {
    String GLUE = ", ";
    StringBuilder sb = new StringBuilder();

    for (Map.Entry e : encodingsScores.entrySet()) {
        sb.append(e.getKey() + ":" + e.getValue()[0] + GLUE);
    }
    int len = sb.length();
    sb.delete(len - GLUE.length(), len);

    return "{ " + sb.toString() + " }";
}

改进:guessEncoding方法完全读取输入流.对于大型输入流,这可能是一个问题.所有这些库都将读取整个输入流.这意味着用于检测字符集的大量时间消耗.

可以将初始数据加载限制为几个字节,并仅对这几个字节执行字符集检测.



7> 小智..:

上面的库是简单的BOM检测器,当然只有在文件开头有BOM时才能使用.看一下扫描文本的http://jchardet.sourceforge.net/


只是在小费,但这个网站上没有"上面" - 考虑说明你所指的库.

8> 小智..:

我找到了一个很好的第三方库,它可以检测实际编码:http: //glaforge.free.fr/wiki/index.php?wiki = GuessEncoding

我没有广泛测试它,但它似乎工作.



9> ssamuel68..:

如果您使用ICU4J(http://icu-project.org/apiref/icu4j/)

这是我的代码:

            String charset = "ISO-8859-1"; //Default chartset, put whatever you want

            byte[] fileContent = null;
            FileInputStream fin = null;

            //create FileInputStream object
            fin = new FileInputStream(file.getPath());

            /*
             * Create byte array large enough to hold the content of the file.
             * Use File.length to determine size of the file in bytes.
             */
            fileContent = new byte[(int) file.length()];

            /*
             * To read content of the file in byte array, use
             * int read(byte[] byteArray) method of java FileInputStream class.
             *
             */
            fin.read(fileContent);

            byte[] data =  fileContent;

            CharsetDetector detector = new CharsetDetector();
            detector.setText(data);

            CharsetMatch cm = detector.detect();

            if (cm != null) {
                int confidence = cm.getConfidence();
                System.out.println("Encoding: " + cm.getName() + " - Confidence: " + confidence + "%");
                //Here you have the encode name and the confidence
                //In my case if the confidence is > 50 I return the encode, else I return the default value
                if (confidence > 50) {
                    charset = cm.getName();
                }
            }

记得把所有尝试捕获需要它.

我希望这适合你.



10> faghani..:

据我所知,在这种情况下没有通用的库适合所有类型的问题。因此,对于每个问题,您都应该测试现有的库并选择可以满足您的问题约束的最佳库,但是通常都不适合。在这些情况下,您可以编写自己的编码检测器!正如我写的...

我编写了一个元Java工具,使用IBM ICU4j和Mozilla JCharDet作为内置组件来检测HTML网页的字符集编码。在这里您可以找到我的工具,请先阅读自述文件部分。另外,您可以在我的论文及其参考文献中找到此问题的一些基本概念。

在贝娄,我提供了一些有益的意见,这些意见对我的工作很有帮助:

字符集检测不是一个万无一失的过程,因为它基本上是基于统计数据的,而实际发生的事情是猜测没有检测到

在这种情况下,icu4j是IBM imho的主要工具

TikaEncodingDetector和Lucene-ICU4j都使用了icu4j,它们的准确性与我的测试中的icu4j并没有太大的区别(我记得最多为%1)

icu4j比jchardet通用得多,icu4j稍微偏向于IBM系列编码,而jchardet偏向于utf-8

由于UTF-8在HTML世界中的广泛使用;总体而言,jchardet是比icu4j更好的选择,但不是最佳选择!

icu4j非常适合东亚特定的编码,例如EUC-KR,EUC-JP,SHIFT_JIS,BIG5和GB系列编码

icu4j和jchardet都无法处理Windows-1251和Windows-1256编码的HTML页面。Windows-1251 aka cp1251广泛用于基于西里尔文的语言,例如俄语,而Windows-1256 aka cp1256广泛用于阿拉伯语

几乎所有的编码检测工具都使用统计方法,因此输出的准确性很大程度上取决于输入的大小和内容

某些编码本质上是相同的,只是存在部分差异,因此在某些情况下,猜测或检测到的编码可能为假,但同时为真!关于Windows-1252和ISO-8859-1。(请参阅我的论文5.2节的最后一段)

推荐阅读
ar_wen2402851455
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有