7
回答
java使用jchardet检测文本文件(字节流)的编码方式
利用AWS快速构建适用于生产的无服务器应用程序,免费试用12个月>>>   

有时需要InputStreamReader(InputStream in, Charset cs)这个构造来处理字符流。然而Charset不一定知道。这个时候就需要检测编码方式了。jchardet是firefox使用的字节流编码检测算法 的java开源实现,协议为MPL(Mozilla Public License),对商业友好。下载源代码后发现示例并不怎么好使用,于是封装了一下。下面就封装类和使用Demo。

CharsetDetector 这个封装了内部实现,用户直接new这个类就可以检测字节流编码

package cn.xddai.chardet;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.mozilla.intl.chardet.nsDetector;
import org.mozilla.intl.chardet.nsICharsetDetectionObserver;
import org.mozilla.intl.chardet.nsPSMDetector;

/**
 *
 * @author xddai
 */
public class CharsetDetector
{

    private boolean found = false;
    private String result;
    private int lang;

    public String[] detectChineseCharset(InputStream in) throws IOException
    {
        lang = nsPSMDetector.CHINESE;
        String[] prob;
        // Initalize the nsDetector() ;
        nsDetector det = new nsDetector(lang);
        // Set an observer...
        // The Notify() will be called when a matching charset is found.

        det.Init(new nsICharsetDetectionObserver()
        {

            public void Notify(String charset)
            {
                found = true;
                result = charset;
            }
        });
        BufferedInputStream imp = new BufferedInputStream(in);
        byte[] buf = new byte[1024];
        int len;
        boolean isAscii = true;
        while ((len = imp.read(buf, 0, buf.length)) != -1)
        {
            // Check if the stream is only ascii.
            if (isAscii)
                isAscii = det.isAscii(buf, len);
            // DoIt if non-ascii and not done yet.
            if (!isAscii)
            {
                if (det.DoIt(buf, len, false))
                    break;
            }
        }
        imp.close();
        in.close();
        det.DataEnd();
        if (isAscii)
        {
            found = true;
            prob = new String[]
                    {
                        "ASCII"
                    };
        } else if (found)
        {
            prob = new String[]
                    {
                        result
                    };
        } else
        {
            prob = det.getProbableCharsets();
        }
        return prob;
    }

    public String[] detectAllCharset(InputStream in) throws IOException
    {
        try
        {
            lang = nsPSMDetector.ALL;
            return detectChineseCharset(in);
        } catch (IOException e)
        {
            throw e;
        }
    }
}

Demo:这个演示CharsetDetector用法示例

package cn.xddai.chardet;

import java.io.IOException;
import java.net.URL;

/**
 *
 * @author xddai
 */
public class Demo
{
    public static void main(String[] args)throws IOException
    {
        CharsetDetector charDect = new CharsetDetector();
        URL url = new URL("http://www.oschina.net/");
        String[] probableSet = charDect.detectChineseCharset(url.openStream());
        for (String charset : probableSet)
        {
            System.out.println(charset);
        }
    }
}

封装后的jar包下载地址 http://codeinplatform.googlecode.com/files/CharsetDetector.jar

举报
鉴客
发帖于6年前 7回/10K+阅
共有7个评论 最后回答: 3年前

用过jchardet一段时间,感觉有时也不太灵.

很想知道Google Chrome 用什么来检测文件编码

常常用Chrome 打开文本文件,一直挺不错的

Firefox 打开大点的文本文件就卡死了..

引用来自“鉴客”的答案

这段代码的问题在于如果编码选择是 Unicode ,则检测的结果是 windows-1252

使用cpdetector最新包,1.0.10,调整detector的顺序为以下。

CodepageDetectorProxy detector = CodepageDetectorProxy.getInstance();
detector.add(UnicodeDetector.getInstance());
detector.add(JChardetFacade.getInstance());
detector.add(ASCIIDetector.getInstance());
File file = new File(filepath);
Charset charset = detector.detectCodepage(file.toURI().toURL());
可正确解码。

引用来自“花谢花开”的评论

引用来自“鉴客”的答案

这段代码的问题在于如果编码选择是 Unicode ,则检测的结果是 windows-1252

使用cpdetector最新包,1.0.10,调整detector的顺序为以下。

CodepageDetectorProxy detector = CodepageDetectorProxy.getInstance();
detector.add(UnicodeDetector.getInstance());
detector.add(JChardetFacade.getInstance());
detector.add(ASCIIDetector.getInstance());
File file = new File(filepath);
Charset charset = detector.detectCodepage(file.toURI().toURL());
可正确解码。

这段代码可以检查大多数windows平台下文本文件的编码。但是有一个疑问就是UnicodeDetector和JChardetFacade好像重复了。我试了一下,单独删除JChardetFacade或者单独删除UnicodeDetector都可以识别。

另外,如果在windows下新建一个txt文件,只输入2、3个汉字跟一个英文字母,会被误判为UTF-16LE

--- 共有 1 条评论 ---
花谢花开删除JChardet的话,GB18030的你试过么? 3年前 回复

再一次进行了实验,兼回复@花谢花开。结论稍有不同,在windows系统中共测试了21个文本文件(http://pan.baidu.com/s/1bnwBt9t),包括txt、properties、xml三种格式,现报告如下。

  • UnicodeDetector + ASCIIDetector

GB18030文件识别不出;新建文本文件(ANSI)只输入2、3个字符后且开头中文识别不出;另存为为ANSI的文件识别不出UTF-8无BOM文件识别不出。

  • JChardetFacade + ASCIIDetector

新建文本文件(ANSI)只输入2、3个字符后且开头中文误识别为UTF-16LE;另存为为ANSI的文件识别为UTF-16LE;UTF-16LE文件误识别为windows-1252。

  • UnicodeDetector + JChardetFacade + ASCIIDetector

新建文本文件(ANSI)只输入2、3个字符后且开头中文误识别为UTF-16LE;另存为为ANSI的文件识别为UTF-16LE;

主要代码TestFileCode.java不完整


再再一次进行了实验,这次的结果很诡异。


这次用到的是官方提供的测试文档cpdetector_testdocuments_1.0.10中的xml.ascc.net文件夹

我是解压后再copy到另一个盘里面,然后用同样的程序,使用UnicodeDetector + JChardetFacade + ASCIIDetector三个解码器,得到的结果很让人膝盖疼。求高人解答

顶部