咱們知道從一個文件流中讀取內容時是要指定具體的編碼格式的,不然讀出來的內容會是亂碼。好比咱們的代碼寫成下面這個樣子: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; } }