我们知道从一个文件流中读取内容时是要指定具体的编码格式的,否则读出来的内容会是乱码。比如我们的代码写成下面这个样子:
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的文件,那么得到的内容将是一坨乱码。
上面的问题解决起来很简单,只要指定下字符编码就可以了。
new string(bytes,"gbk");
在告知文件编码格式的条件下,解决上面的问题是很简单。假如现在没告知文件具体的编码格式,我们需要怎么正确的读取文件呢?一个可行的办法是推测文件编码方式。
网上有多种方式可以“推测”出一个文件的可用编码,但是需要注意的是:所有的方法都不能保证推测出来的结果是绝对准确的,有的方法推测的准确率较高,而有的方法推测出来的准确率较低。主要的推测方法有以下几种:
下面就来具体介绍下怎么使用cpdector
和icu4j
推测文件编码。
使用cpdetector jar包,提供两种方式检测文件编码,至于选择哪种 需要根据个人需求,文档有注释。依赖antlr-2.7.4.jar,chardet-1.0.jar,jargs-1.0.jar三个jar包。 可以再官网下载 http://cpdetector.sourceforge.net/。
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; /** * <p> * 获取流编码,不保证完全正确,设置检测策略 isfast为true为快速检测策略,false为正常检测 * inputstream 支持mark,则会在检测后调用reset,外部可重新使用。 * inputstream 流没有关闭。 * </p> * * <p> * 如果采用快速检测编码方式,最多会扫描8个字节,依次采用的{@link unicodedetector},{@link byteordermarkdetector}, * {@link jchardetfacade}, {@link asciidetector}检测。对于一些标准的unicode编码,适合这个方式或者对耗时敏感的。 * </p> * * <p> * 采用正常检测,读取指定字节数,如果没有指定,默认读取全部字节检测,依次采用的{@link byteordermarkdetector},{@link parsingdetector},{@link jchardetfacade}, {@link asciidetector}检测。 * 字节越多检测时间越长,正确率较高。 * </p> * @author wukong * */ public class cpdetectorencoding { private static final logger logger = logger.getlogger(cpdetectorencoding.class); /** * <p> * 获取流编码,不保证完全正确,设置检测策略 isfast为true为快速检测策略,false为正常检测 * inputstream 支持mark,则会在检测后调用reset,外部可重新使用。 * inputstream 流没有关闭。 * </p> * * <p> * 如果采用快速检测编码方式,最多会扫描8个字节,依次采用的{@link unicodedetector},{@link byteordermarkdetector}, * {@link jchardetfacade}, {@link asciidetector}检测。对于一些标准的unicode编码,适合这个方式或者对耗时敏感的。 * </p> * * <p> * 采用正常检测,读取指定字节数,如果没有指定,默认读取全部字节检测,依次采用的{@link byteordermarkdetector},{@link parsingdetector},{@link jchardetfacade}, {@link asciidetector}检测。 * 字节越多检测时间越长,正确率较高。 * </p> * * @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; } }
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的功能主要有:
代码示例:
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; } }
如对本文有疑问, 点击进行留言回复!!
Springboot项目因为kackson版本问题启动报错解决方案
Java多线程下的其他组件之CyclicBarrier、Callable、Future和FutureTask详解
网友评论