理想论坛_专业20年的财经股票炒股论坛交流社区 - 股票论坛

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 3082|回复: 0

没提供编码格式,读文件时要怎么推测文件具体的编码

[复制链接]

9650

主题

9650

帖子

2万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
28966
发表于 2019-12-27 14:23 | 显示全部楼层 |阅读模式
引子

我们晓得从一个文件流中读取内容时是要指定具体的编码格式的,否则读出来的内容会是乱码。比如我们的代码写成下面这个样子:
  1. private static void m1(){    try(FileInputStream fileInputStream = new FileInputStream("D:\\逐日摘录.txt")) {        byte[] bytes = FileCopyUtils.copyToByteArray(fileInputStream);        System.out.println(new String(bytes));    } catch (FileNotFoundException e) {        e.printStackTrace();    } catch (IOException e) {        e.printStackTrace();    }}
复制代码
实行上面的代码,偶然我们能“侥幸”获得正确的实行成果。由于new String(byte[])这个方式会指定默许的编码格式,所以假如我们读取的文件的编码格式恰好是UTF8的话,那上面的代码就一点题目没有。可是假如我们读取的是一个编码格式是GBK的文件,那末获得的内容将是一坨乱码。
上面的题目治理起来很简单,只要指定下字符编码便可以了。
  1. new String(bytes,"GBK");
复制代码
在奉告文件编码格式的条件下,治理上面的题目是很简单。假如现在没奉告文件具体的编码格式,我们需要怎样正确的读取文件呢?一个可行的法子是猜测文件编码方式。
猜测文件编码的方式

网上有多种方式可以“猜测”出一个文件的可用编码,可是需要留意的是:全数的方式都不能保证猜测出来的成果是绝对正确的,有的方式猜测的正确率较高,而有的方式猜测出来的正确率较低。重要的猜测方式有以下几种:

  • 经过文件的前三个字节来判定:由于有些编码格式会存在文件的前面3个字节中,比如UTF-8编码格式的文本文件,其前3个字节的值就是-17、-69、-65。可是很明显,这类方式的范围性比力大,猜测出来的正确率也比力低,是以不举荐这类方式。
  • 经过出格字符来判定:经过某些编码格式编码的文件中会出现一些特此外字节值,是以可以经过判定文件中能否有这些出格值来猜测文件编码格式。此朴直确率也不高,不举荐操纵。
  • 经过工具库cpdetector来判定:cpdector 是一款开源的文档编码检测工具,可以检测 xml,html文档编码典范。是基于统计学道理来猜测文件编码的,可是也不保证猜测成果的正确性。
  • 经过ICU4J库来判定:ICU的猜测逻辑基于IBM过去几十年收集的字符集数据,理论上也是基于统计学的。这类方式统计的成果正确性也较高举荐操纵。
下面就来具体先容下怎样操纵cpdector和ICU4J猜测文件编码。
cpdector

操纵Cpdetector jar包,供给两种方式检测文件编码,至于挑选哪类 需要按照小我需求,文档有诠释。依靠antlr-2.7.4.jar,chardet-1.0.jar,jargs-1.0.jar三个jar包。 可以再官网下载 http://cpdetector.sourceforge.net/。
  1. import info.monitorenter.cpdetector.io.ASCIIDetector;import info.monitorenter.cpdetector.io.ByteOrderMarkDetector;import info.monitorenter.cpdetector.io.CodepageDetectorProxy;import info.monitorenter.cpdetector.io.JChardetFacade;import info.monitorenter.cpdetector.io.ParsingDetector;import info.monitorenter.cpdetector.io.UnicodeDetector;import java.io.BufferedInputStream;import java.io.ByteArrayInputStream;import java.io.IOException;import java.io.InputStream;import java.nio.charset.Charset;import org.apache.log4j.Logger;/** *  *  获得流编码,不保证完全正确,设备检测计谋 isFast为true为快速检测计谋,false为一般检测 *  InputStream 支持mark,则会在检测后挪用reset,内部可重新操纵。 *  InputStream 流没有封闭。 *
  2. *  *  *  假如采纳快速检测编码方式,最多会扫描8个字节,依次采纳的{@link UnicodeDetector},{@link byteOrderMarkDetector}, *  {@link JChardetFacade}, {@link ASCIIDetector}检测。对于一些标准的unicode编码,适当这个方式大要对耗时敏感的。 *
  3. *  *  *  采纳一般检测,读取指定字节数,假如没有指定,默许读取全数字节检测,依次采纳的{@link byteOrderMarkDetector},{@link parsingDetector},{@link JChardetFacade}, {@link ASCIIDetector}检测。 *  字节越多检测时候越长,正确率较高。 *
  4. * @author WuKong * */public class CpdetectorEncoding {        private static final Logger logger = Logger.getLogger(CpdetectorEncoding.class);        /**     *      * 获得流编码,不保证完全正确,设备检测计谋 isFast为true为快速检测计谋,false为一般检测     * InputStream 支持mark,则会在检测后挪用reset,内部可重新操纵。     * InputStream 流没有封闭。     *
  5.      *      *      * 假如采纳快速检测编码方式,最多会扫描8个字节,依次采纳的{@link UnicodeDetector},{@link byteOrderMarkDetector},     * {@link JChardetFacade}, {@link ASCIIDetector}检测。对于一些标准的unicode编码,适当这个方式大要对耗时敏感的。     *
  6.      *      *      *  采纳一般检测,读取指定字节数,假如没有指定,默许读取全数字节检测,依次采纳的{@link byteOrderMarkDetector},{@link parsingDetector},{@link JChardetFacade}, {@link ASCIIDetector}检测。     *  字节越多检测时候越长,正确率较高。     *
  7.      *     * @param in 输入流  isFast 能否采纳快速检测编码方式     * @return Charset The character are now - hopefully - correct。假如为null,没有检测出来。     * @throws IOException      */    public Charset getEncoding(InputStream buffIn,boolean isFast) throws IOException{                return getEncoding(buffIn,buffIn.available(),isFast);    }        public Charset getFastEncoding(InputStream buffIn) throws IOException{        return getEncoding(buffIn,MAX_READBYTE_FAST,DEFALUT_DETECT_STRATEGY);    }                public Charset getEncoding(InputStream in, int size, boolean isFast) throws IOException {                try {                        java.nio.charset.Charset charset = null;                        int tmpSize = in.available();            size = size >tmpSize?tmpSize:size;            //if in support mark method,             if(in.markSupported()){                                if(isFast){                                        size = size>MAX_READBYTE_FAST?MAX_READBYTE_FAST:size;                    in.mark(size++);                    charset = getFastDetector().detectCodepage(in, size);                }else{                                        in.mark(size++);                    charset = getDetector().detectCodepage(in, size);                }                in.reset();                            }else{                                if(isFast){                                        size = size>MAX_READBYTE_FAST?MAX_READBYTE_FAST:size;                    charset = getFastDetector().detectCodepage(in, size);                }else{                    charset = getDetector().detectCodepage(in, size);                }            }                                    return charset;        }catch(IllegalArgumentException e){                        logger.error(e.getMessage(),e);            throw e;        } catch (IOException e) {                        logger.error(e.getMessage(),e);            throw e;        }            }            public Charset getEncoding(byte[] byteArr,boolean isFast) throws IOException{                return getEncoding(byteArr, byteArr.length, isFast);    }            public Charset getFastEncoding(byte[] byteArr) throws IOException{                return getEncoding(byteArr, MAX_READBYTE_FAST, DEFALUT_DETECT_STRATEGY);    }            public Charset getEncoding(byte[] byteArr, int size,boolean isFast) throws IOException {                size = byteArr.length>size?size:byteArr.length;        if(isFast){            size = size>MAX_READBYTE_FAST?MAX_READBYTE_FAST:size;        }                ByteArrayInputStream byteArrIn = new ByteArrayInputStream(byteArr,0,size);        BufferedInputStream in = new BufferedInputStream(byteArrIn);                try {                        Charset charset = null;            if(isFast){                                charset = getFastDetector().detectCodepage(in, size);            }else{                                charset = getDetector().detectCodepage(in, size);            }                        return charset;        } catch (IllegalArgumentException e) {                        logger.error(e.getMessage(),e);            throw e;        } catch (IOException e) {                        logger.error(e.getMessage(),e);            throw e;        }           }        private static CodepageDetectorProxy detector =null;    private static CodepageDetectorProxy fastDtector =null;    private static ParsingDetector parsingDetector =  new ParsingDetector(false);    private static ByteOrderMarkDetector byteOrderMarkDetector = new ByteOrderMarkDetector();        //default strategy use fastDtector    private static final boolean DEFALUT_DETECT_STRATEGY = true;        private static final int MAX_READBYTE_FAST = 8;         private static CodepageDetectorProxy getDetector(){                if(detector==null){                        detector = CodepageDetectorProxy.getInstance();             // Add the implementations of info.monitorenter.cpdetector.io.ICodepageDetector:             // This one is quick if we deal with unicode codepages:            detector.add(byteOrderMarkDetector);            // The first instance delegated to tries to detect the meta charset attribut in html pages.            detector.add(parsingDetector);            // This one does the tricks of exclusion and frequency detection, if first implementation is             // unsuccessful:            detector.add(JChardetFacade.getInstance());            detector.add(ASCIIDetector.getInstance());        }                return detector;    }            private static CodepageDetectorProxy getFastDetector(){                if(fastDtector==null){                        fastDtector = CodepageDetectorProxy.getInstance();            fastDtector.add(UnicodeDetector.getInstance());            fastDtector.add(byteOrderMarkDetector);             fastDtector.add(JChardetFacade.getInstance());            fastDtector.add(ASCIIDetector.getInstance());        }                return fastDtector;    }    }
复制代码
ICU4J

ICU (International Components for Unicode)是为软件利用供给Unicode和举世化支持的一套成熟、普遍操纵的C/C++和Java类库集,可在全数平台的C/C++和Java软件上获得同等的成果。
ICU首先是由Taligent公司开辟的,Taligent公司被合并为IBM公司举世化认证中心的Unicode研讨组后,ICU由IBM和开源机关互助继续开辟。起头ICU只要Java平台的版本,后来这个平台下的ICU类被吸归入SUN公司开辟的JDK1.1,并在JDK今后的版本中不停改良。C++和C平台下的ICU是由JAVA平台下的ICU移植过来的,移植过的版本被称为ICU4C,来支持这C/C++两个平台下的国际化利用。ICU4J和ICU4C区分不大,但由于ICU4C是开源的,而且紧密跟进Unicode标准,ICU4C支持的Unicode标准总是最新的;同时,由于JAVA平台的ICU4J的公布需要和JDK绑定,ICU4C支持Unicode标准改变的速度要比ICU4J快的多。
ICU的功用重要有:

  • 代码页转换: 对文本数据举行Unicode、几乎任何其他字符集或编码的相互转换。ICU的转化表基于IBM过去几十年收集的字符集数据,在全国各地都是最完整的。
  • 排序法则(Collation): 按照特定说话、地域或国家的治理和标准比力字数串。ICU的排序法则基于Unicode排序法则算法加上来自公共地域性数据仓库(Common locale data repository)的地域特定比力法则。
  • 格式化: 按照所选地域设备的老例,实现对数字、货币、时候、日期、和利率的格式化。包含将月和日称号转换成所选说话、挑选适当缩写、正确对字段举行排序等。这些数据也取自公共地域性数据仓库。
  • 时候盘算: 在传统格里历底子上供给多种历法。供给一整套时区盘算API。
  • Unicode支持: ICU紧密跟进Unicode标准,经过它可以很轻易地拜候Unicode标准拟订的很多Unicode字符属性、Unicode标准化、巨细写转换和其他底子操纵。
  • 正则表达式: ICU的正则表达式周全支持Unicode而且性能极具合作力。
  • Bidi: 支持不同翰墨钞缮次第肴杂翰墨(例如从左到右钞缮的英语,大要从右到左钞缮的阿拉伯文和希伯来文)的处置惩罚。
  • 文本界限: 在一段文本内定位词、句或段落位置、或标识最适当表示文本的自动换行位置。
代码示例:
  1. public class FileEncodingDetector {    public static void main(String[] args) {        File file = new File("D:\\xx1.log");        System.out.println(getFileCharsetByICU4J(file));    }    public static String getFileCharsetByICU4J(File file) {        String encoding = null;        try {            Path path = Paths.get(file.getPath());            byte[] data = Files.readAllBytes(path);            CharsetDetector detector = new CharsetDetector();            detector.setText(data);            //这个方式猜测首选的文件编码格式            CharsetMatch match = detector.detect();            //这个方式可以猜测出全数大要的编码方式            CharsetMatch[] charsetMatches = detector.detectAll();            if (match == null) {                return encoding;            }            encoding = match.getName();        } catch (IOException var6) {            System.out.println(var6.getStackTrace());        }        return encoding;    }}
复制代码
留意点


  • ICU4J和cpdector猜测出来的文件编码都不能保证百分百正确,只能保证大要率正确;
  • ICU4J和cpdector猜测出来的编码不愿定是文件原始的编码。比如我的一个文本文件中只要简单的英翰墨符,然后我将这个文件存为GBK编码格式。这时你操纵这两个工具猜测出来的文件编码大如果ASCII编码。可是操纵ASCII编码也能正确翻开这个文件,由于GBK是兼容ASCII的。所以能看出,这两个工具都是以能正确解码文件为原则来猜测编码的,不愿定要猜测出原始编码。
参考


免责声明:假如加害了您的权益,请联系站长,我们会实时删除侵权内容,感谢合作!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|理想论坛_专业20年的财经股票炒股论坛交流社区 - 股票论坛

GMT+8, 2020-7-12 17:31 , Processed in 0.165848 second(s), 28 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表