判斷文件編碼是否為UTF-8收藏


這里研究一下如何來判斷文件的編碼是否是UTF-8,關於這個問題網絡上一般采用的是判斷文件的BOM頭,但是這種方法有個缺點,就是有一些工具,比如EditPlus,比如Java程序,做出來的UTF-8編碼的文件是不會在文件內容的前面加上BOM頭的,對於這種情況,網絡上的這個辦法就會檢測失敗。

 

在經過一些測試之后,研究了一個解決方案。

 

考慮如下文件輸入流的代碼,

 

FileInputStream fis = null;

InputStreamReader isr = null;

BufferedReader br = null;

            

            

File f = new File(fn);

fis = new FileInputStream(f);

isr = new InputStreamReader(fis, "UTF-8");

br = new BufferedReader(isr);

 

推測執行原理如下,(都是根據測試結果的猜測)

 

1。fis 根據文件的保存編碼來采用不同的編碼讀取文件。讀取結果為byte[]

2.isr設定的話,那么根據isr設定的編碼來讀取文件,如果不設定,那么編碼采用系統默認編碼 ansi(window-31j,shift_jis)

3。br.readline,將isr的結果組合為String,如果isr沒有設定編碼,那么組合String時采用的編碼為系統默認編碼 ansi(window-31j,shift_jis),如果isr設定了編碼,那么采用isr設定好的編碼。

4。new string(byte[],"encode")  根據指定編碼生成string,如果不指定,采用系統默認編碼。系統默認編碼 ansi

5。string.getbyte("encode")   從String根據特定的編碼取得byte[]

 

問題出在第1步,第一步fis因為讀取文件的時候,調用的是native,也就是系統(windows系統)的東西,他用了系統的東西,系統的這個東西作了編碼判斷,但是因為他調用的是native的東西,這個判定結果沒有返回給java,導致java里面isr,br沒有辦法跟fis協調一致,isr,br只能采用系統默認編碼 ansi(window-31j,shift_jis),而不是采用fis的判定結果來讀取文件。

 

 

這導致了,當文件使用ansi編碼保存的時候,默認編碼跟fis判定結果一致,不會出任何問題。

 

當文件使用了utf-8編碼的時候,默認編碼ansi,跟fis判定結果utf-8不一致,fis采用uft-8編碼讀取出文件內容,而后,br.readline采用系統默認編碼把UTF-8編碼對應的byte[]組合成了ansi編碼對應的字符串,就產生了亂碼。

 

 

我在網絡以及java api里面查找了一下,沒有找到判定文件保存編碼的方法。推論:因為java是調用了native的方法來實際讀取文件的,判定在native里面完成,但是沒有把判定結果公開給我們程序員。

 

另有一個測試結果的推論,英文字符在任何編碼下面讀取出來的byte[]都是一樣的。因為我們才用任何編碼都不會出現英文字符亂碼的問題,所以大多數時候這個判定對我們沒有影響,這里不討論特殊情況下因為這個原因造成的影響。

  

 

根據以上推論,考慮如下解決問題的思路,

 

1。通過fis來讀取文件,這個時候讀取來的byte[]根據文件的保存格式是不同的。fis會自動判斷處理。

2。通過br來讀取文件。代碼示例如下:

private static void readTest(String fn){

         BufferedReader br = null;

         InputStreamReader isr = null;

         FileInputStream fis = null;

         try{

                 File f = new File(fn);

                 fis = new FileInputStream(f);

                 isr = new InputStreamReader(fis, "UTF-8");

                 br = new BufferedReader(isr);

                 String s = null;

                 while ((s = br.readLine()) != null) {

                         byte[] buf = s.getBytes("UTF-8");

                         for(int j=0; j<buf.length; j++){

                                 System.out.println(buf[j]);

                         }

                 }

         } catch(Exception e){

                 e.printStackTrace();

         } finally{

                 try{

                         fis.close();

                         isr.close();

                 } catch(Exception e){

                         e.printStackTrace();

                 }

         }

}

 

3。1,2的讀取結果byte[]進行比較,如果相同,那么可以認為文件的保存格式為UTF-8(雖然存在全英文ansi保存的可能,但是這種狀況認為是utf-8保存不會有影響),如果不同則不是UTF-8,考慮我們目前狀況,那么不是UTF-8可以認為文件保存編碼就是ANSI,如果不可以這么認為,其他編碼類型也要做這個判斷。因為英文字符在任何編碼下面讀取出來的byte[]都是一樣的。所以這里有一個效率問題,必須文件內容全部比較,不能只比較一部分。

 

以上推測經測試有效,沒有問題。

如果使用第三方開源代碼common-io.jar的話,可以將以上思路簡化為如下代碼。

public boolean isUTF8(File file){

       

try {

        byte[] buf = FileUtils.readFileToByteArray(file);

        String filecCntent = FileUtils.readFileToString(file,"UTF-8");

        if(filecCntent.equals(new String(buf,"UTF-8"))){

                return true;

        }

} catch (IOException e) {

        // TODO 動生成された catch ブロック

        e.printStackTrace();

}

return false

}

 

 

這個判定有一個效率問題,在這個文章中采用的是讀取整個文件,如果我們文件太大,會比較花時間。這種情況可以考慮按行來讀取判定,某一行不一致就可以退出了。這樣可以提高一些效率。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM