zoukankan      html  css  js  c++  java
  • 纯文本-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。

     

  • 相关阅读:
    .NET Interop 工具集
    关于正弦波的算法
    Windows Phone 系列 本地数据存储
    Xaml cannot create an instance of “X”
    Windows Phone 系列 使用 MVVM绑定时无法获取当前值
    Windows Phone 系列 应用程序图标无法显示
    Windows Phone 系列 WPConnect无法上网的问题
    Windows Phone 系列 使用 Windows Phone 保存铃声任务
    WP7.5提交应用
    Windows Phone 系列 动态删除ObservableCollection
  • 原文地址:https://www.cnblogs.com/pxb2018/p/10738758.html
Copyright © 2011-2022 走看看