咱们知道从一个文件流中读取内容时是要指定具体的编码格式的,不然读出来的内容会是乱码。好比咱们的代码写成下面这个样子:html
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的文件,那么获得的内容将是一坨乱码。java
上面的问题解决起来很简单,只要指定下字符编码就能够了。正则表达式
new String(bytes,"GBK");
在告知文件编码格式的条件下,解决上面的问题是很简单。假如如今没告知文件具体的编码格式,咱们须要怎么正确的读取文件呢?一个可行的办法是推测文件编码方式。算法
网上有多种方式能够“推测”出一个文件的可用编码,可是须要注意的是:全部的方法都不能保证推测出来的结果是绝对准确的,有的方法推测的准确率较高,而有的方法推测出来的准确率较低。主要的推测方法有如下几种:apache
下面就来具体介绍下怎么使用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软件上得到一致的结果。ui
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的功能主要有:.net
代码示例:
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; } }