純文本-FileInputStream的編碼與解碼方式



前言:以下分析只針對純文本


 1.FileInputStream默認的編碼方式就是文件的編碼方式

即:源文件是什么編碼方式,則利用FileInputStream默認讀取的字節數組,就是什么編碼方式。

例:純文本采用“GBK”編碼,文本內容如下(注意:文本是純漢字):

你好世界我是潘小白

利用“GBK”字符集解碼如下:

package cn.edu.uestc.IO;

import java.io.*;

public class TestFileInputStream03 {
    public static void main(String[] args){
        //
        File file = new File("abc3.txt");
        //
        InputStream is = null;
        try {
            is = new FileInputStream(file);
            //操作
            byte[] bytes = new byte[4];//這里數組容量必須采用2的倍數,具體原因后面后談
            int len = -1;
            while ((len = is.read(bytes))!=-1){
                String str = new String(bytes,0,len,"GBK");//利用GBK字符集,對FileInputStream讀取的字節數組進行解碼
                System.out.print(str);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            //釋放資源
            try {
                if (null!=is){
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}
/*output:
你好世界我是潘小白
*/

分析:通過代碼可知,我采用FileInputStream對格式為“GBK”的純漢字文本讀取,得到的字節數組,可以用"GBK"字符集對其完美解碼;反推可知,FileInputStream默認讀取的字節數組,其編碼格式和原文件編碼格式相同。接下來,用"UTF-8"對其進行解碼試一試。。。

利用“UTF-8”字符集解碼如下:

package cn.edu.uestc.IO;

import java.io.*;

public class TestFileInputStream03 {
    public static void main(String[] args){
        //
        File file = new File("abc3.txt");
        //
        InputStream is = null;
        try {
            is = new FileInputStream(file);
            //操作
            byte[] bytes = new byte[4];//這里數組容量采用3的倍數,區別於上面GBK解碼時2的倍數,具體原因后面談
            int len = -1;
            while ((len = is.read(bytes))!=-1){
                String str = new String(bytes,0,len,"UTF-8");//利用UTF-8字符集對字節數組進行解碼
                System.out.print(str);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            //釋放資源
            try {
                if (null!=is){
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}
/*output:
�������������С��//輸出無法解碼
*/

 分析:利用UTF-8無法解碼,再次說明,FileInputStream默認讀取的字節數組的編碼格式,就是原文件的編碼格式。

同理讀者可以將純文本(純漢字文本)設置成UTF-8的編碼格式,再分別采用“GBK”和“UTF-8”方式解碼試一試,特別注意數組容量的選擇,即:“純漢字文本,GBK解碼時,字節數組容量是2的倍數”、““純漢字文本,UTF-8解碼時,字節數組容量是3的倍數”,原因下面分析。

————————簡單的分割————————

2.采用“GBK”對純漢字文本解碼時,字節數組容量是2的倍數;“UTF-8”對純漢字文本解碼時,字節數組容量是3的倍數。

原因是:“GBK”編碼時,一個漢字是2個字節,“UTF-8”對常規漢字編碼時,一個漢字是3個字節(UTF-8方式下,生僻漢字也可能會占4個字節,這種方式此處不談)。

所以,你要對字節數組解碼時,你首先必須成組的取字節(“GBK”模式下2的倍數一組,“UTF-8”模式下3的倍數一組),否則會將一個漢字的字節拆開,這樣肯定會亂碼,其對應着我上一篇文章提到的“字節數不全或者丟失情況,產生的亂碼”。

 此處,我們用代碼做一下簡單示范,原文本采用“GBK”編碼,字節數組容量采用3,不是2的倍數:

package cn.edu.uestc.IO;

import java.io.*;

public class TestFileInputStream03 {
    public static void main(String[] args){
        //
        File file = new File("abc3.txt");
        //
        InputStream is = null;
        try {
            is = new FileInputStream(file);
            //操作
            byte[] bytes = new byte[3];//不是2的倍數
            int len = -1;
            while ((len = is.read(bytes))!=-1){
                String str = new String(bytes,0,len,"GBK");//卻用GBKJ解碼
                System.out.print(str);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            //釋放資源
            try {
                if (null!=is){
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}
/*output:
你�檬�界�沂�潘�“�//也就第一個字取全了,解碼出來,但是后面字節數亂了,也就無法解碼了
*/

結果看到,基本全部亂碼。

同理,讀者可以采用UTF-8的文本,而設置字節數組容量不是3的倍數,從而進行UTF-8解碼,試試看;你會發現,即使編碼-解碼的字符集同步,但是字節數組中字節個數不對,同樣亂碼。

——————簡單的分割線——————

上面問題2中,“編碼-解碼的字符集同步,字節數組中字節個數不匹配出現亂碼”可以進一步延伸;

我們看到上面,都是純漢字文本,沒有任何英文字符(包括英文字母和英文標點),如果文本是,中英文混合怎么辦,還能否采用上面的方式,對FileInputStream讀取的字節進行解碼呢??

答案是:不能,見下面分析

3.中英文混合純文本,用FileInputStream讀取時,得到的字節數組無法采用上面 String str = new String(bytes,0,length,"CharacterSet")方式解碼,應該采用字符轉換流InputStreamReader。

(提示:這里不再考慮標點符號的事了,你可以將英文標點符號看出一個英文字母,中文下的標點看成一個普通漢字分析,因為同一種編碼格式下,中文字母和中文標點占用字節數一樣,英文字母和英文標點占用字節數一致)

原因:無論是"GBK"還是"UTF-8",英文占用1個字節,所以,當插入引文時,一定會改變字節個數混亂,無法保證“在GBK格式下,每個漢字的兩個字節同時被字節數組讀取”,也無法保證“在UTF-8格式下,每個漢字的三個字節同時被字節數組讀取”,那么將導致后期解碼時,出現亂碼。

示例:文本格式是“GBK”,文本中插入了一個英文字母

你好p世界我是潘小白

 代碼如下:

package cn.edu.uestc.IO;

import java.io.*;

public class TestFileInputStream03 {
    public static void main(String[] args){
        //
        File file = new File("abc3.txt");
        //
        InputStream is = null;
        try {
            is = new FileInputStream(file);
            //操作
            byte[] bytes = new byte[2];//字節數組容量采用2
            int len = -1;
            while ((len = is.read(bytes))!=-1){
                String str = new String(bytes,0,len,"GBK");//GBK解碼,實現編碼-解碼格式匹配
                System.out.print(str);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            //釋放資源
            try {
                if (null!=is){
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
/*output:
你好p�瀾縹沂橋誦“�
*/

結果分析:輸出結果從字母p以后,出現亂碼;這里選取得字節數組的容量是2,所以前兩個漢字被一一讀取,並完美解碼,但是讀取字母p得時候,因為其只占用一個字節,所以漢字“世”被取一個字節,留下一個字節,未被取走,所以導致“世”字無法被正確解碼,而且這也引發連鎖效應,后面得字都將被錯誤得讀取,從而亂碼。

總之:這也是亂碼得一種情況,即“字節數丟失或者不完整造成亂碼”。

這里,可能有人會有疑問,“如果將字節數組容量設置非常大,一次將中英文混合文本全部讀取,然后再解碼,這樣不出現文字多次讀取,造成漢字字節截斷得情況,不就行了嗎?”

是的,這種情況可以實現正確解碼,但是如果文本超級大,這種方式是不現實得,因為字節數組得容量過大,不現實,還是乖乖的用字符轉換流InputStreamReader吧

下面用一個超大字節數組,將文本一次讀取,並完美解碼得代碼示例:

package cn.edu.uestc.IO;

import java.io.*;

public class TestFileInputStream03 {
    public static void main(String[] args){
        //
        File file = new File("abc3.txt");
        //
        InputStream is = null;
        try {
            is = new FileInputStream(file);
            //操作
            byte[] bytes = new byte[20];//數組容量超級大,一次能將中英混合文本全部讀取完
            int len = -1;
            while ((len = is.read(bytes))!=-1){
                String str = new String(bytes,0,len,"GBK");
                System.out.print(str);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            //釋放資源
            try {
                if (null!=is){
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
/*output:
你好p世界我是潘小白//完美解碼
*/

 除了上面的情況,讀者也可以試試,對於中英文結合的文檔,采用UTF-8編碼-解碼;或者故意將英文字體排布規則,即GBK格式下,2個英文一起排列,放在中文文本中,或者GBK格式下,2個英文一起排列,放在中文文本中。對其進行編碼和解碼,並分析一下原因。

補充一點:UTF-8編碼格式下,一些生僻漢字占4個字節,所以將字節數組容量設置成3的倍數時,面對有生僻字的純漢字文本,解碼時也會出現亂碼情況。

——————分割線——————

總結:

上面討論的三個問題,問題1就是屬於編碼-解碼字符集匹配問題,只是進一步說明了FileInputStream讀取的字節數組是哪種編碼方式;

問題2和3,是討論在編碼-解碼字符集匹配情況下,字節個數不完整或者丟失時,解碼時出現亂碼的情況,從而說明了用FileInputStream讀取時,得到的字節數組無法采用上面 String str = new String(bytes,0,length,"CharacterSet")方式解碼,應該采用字符轉換流InputStreamReader。

 


免責聲明!

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



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