• 纯文本-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。

     

  • 相关阅读:
    Swift开发第六篇——操作运算符也可以重载& func 的参数修饰
    Swift开发第五篇——四个知识点(Struct Mutable方法&Tuple&autoclosure&Optional Chain)
    Swift开发第四篇——柯里化
    Swift开发第三篇——Playground
    Swift开发第一篇——异常处理及断言
    在Linux CentOS 6.5 (Final)上安装git-1.9.0
    如何有效地配置基于Spring的应用系统
    关于URL编码的问题
    如何优化pom依赖项?
    启动Tomcat的几种方式
  • 原文地址:https://www.cnblogs.com/pxb2018/p/10738758.html
Copyright © 2020-2023  润新知