纯文本-FileInputStream的编码与解码方式

2019/04/19 21:00
阅读数 562

前言:以下分析只针对纯文本


 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。

 

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部