美文网首页
Java IO流与编码解码

Java IO流与编码解码

作者: 快给我饭吃 | 来源:发表于2019-08-24 18:53 被阅读0次

    最近使用Java编写一些读取文件的小工具的时候,经常与IO流打交道,但是自己对IO流的理解不是特别深刻,因此趁着周末,温故一下IO相关的原理和操作。
    这里我想先说明一下Java IO流和编码解码的总体关系,知道总体再来细讲局部,理解起来会比较方便。强烈推荐本文最后边参考资料第一个引用,一个知乎作者的回答,链接:https://www.zhihu.com/question/39262026/answer/127103286

    一. Java的IO流和编码解码的关系

    我们知道,Java在内存中,都是以两个字节的Unicode编码来存储文字的(不管是中文还是英文或其他文字),但是,程序写入到文件中就有多种格式了,如中文可以选择占两个字节的GB2312编码,也可以选择占三个字节的UTF-8编码(英文字符UTF-8编码占一个字节)。如下图:
    从硬盘中的文件转为内存中的字节,叫解码;从内存中的字节持久化到硬盘中,叫编码。


    文件编码解码

    步骤一:文件1是以UTF-8方式编码的,所以需要对它以UTF-8解码,变成内存中的Unicode字符。如果这时以GB2312等其他方式解码,则会产生乱码。
    步骤二:内存中的Unicode字符,可以将其编码为你需要的格式存入文件,如UTF-8的文件2,或者GB2312的文件3。
    步骤四:GB2312编码的文件需要以GB2312解码形成内存中的Unicode字符
    OK,既然知道上面的编码解码关系,那Java是怎么将文件读入内存,又写入到文件中的呢。答案就是IO流,Java中基本的IO流有:

    • 输入
      • 字节流:InputStream
      • 字符流:Reader
    • 输出
      • 字节流:OutputStream
      • 字符流:Writer

    两者之间的转换是InputStreanReader、OutputSteamReader,这两个转换需要传入一个字符集。Java中InputStream是最基本的流,就像上图中文件到内存、内存到文件之间连接的线,而InputStreamReader、OutputStreamWriter则是这个线上面的编码方式,经过转换,Java中就存在了Unicode字符。否则单纯的InputStream读入只是存在了字节,Java并不知道这些字节的含义。如果我们做一个单纯的文件复制的功能,则只需要使用InputStream、OutputStream读入写出即可,因为我们不关心内部的文本内容。转换成Reader后,则可以使用BufferReader的高级功能处理行数据。如下图,InputStreamReader构造器传入IpnutStream类和字符集Charset类,内部通过StreamDecoder进行解码。

    Java字符集

    二. Java IO流

    基本的文件IO

    程序员对IO流应该都不陌生,Java把不同的输入/输出源(键盘,文件,网络连接)等抽象为“流”。Java通过流的形式来访问不同的输入和输出源。一般的Java读取文件并做处理的代码如下:架设InputStream字节管道,使用InputStreamReader将字节解析为字符Reader,内存中现在保存了Unicode编码,使用BufferedReader处理行。

        @Test
        public void contextLoads() {
    
            // 指定文件
            File f = new File("C:\\Users\\huang\\Desktop\\io.txt");
            try{
                // 架设管道,读入字节流
                InputStream is = new FileInputStream(f);
                // 字节流解码为unicode
                Reader reader = new InputStreamReader(is, "gb2312");
                // 使用BufferedReader方便读取一行
                BufferedReader br = new BufferedReader(reader);
                while (true){
                    String line = br.readLine();
                    if(line == null){
                        break;
                    }
                    System.out.println(line);
                }
    
            }catch (Exception e){
                e.printStackTrace();
            }
    
        }
    

    InputStreamReader源码如下:


    InputStreamReader

    解码是由StreamDecoder处理的,如果不传字符集,默认查找环境变量file.encoding。否则默认UTF-8


    默认编码

    二. Java编码解码

    内存String编码

    java中的char类型用来保存一个unicode字符,故它的长度是两个字节。String str = "我爱中国,I love China";在内存中是Unicode编码的,如果要把他保存到文件中,就需要指定编码方式。以下是输入这些文字的Unicode值

        @Test
        public void contextLoads() {
    
            String str = "我爱中国 China";
            for(int i = 0; i < str.length(); i++){
                char c = str.charAt(i);
                System.out.println(c + " " + Integer.toHexString((int)c));
            }
        }
    

    运行结果如下,中的unicode编码值为4e2d,国的为56fd,如果使用System.out.println("\u4e2d\u56fd");可以输出中国两字。


    Unicode编码

    如下图,而如果指定编码,则gb2312和utf-8长度是不一致的。
    utf-8编码后如下:

        @Test
        public void contextLoads() throws UnsupportedEncodingException {
    
            String str = "我爱中国 China";
            for(int i = 0; i < str.length(); i++){
                char c = str.charAt(i);
                byte[] bytes = (c + "").getBytes("utf-8");
                String tmp = "";
                for(byte b : bytes){
                    tmp += Integer.toHexString(b & 0x00ff) + " ";
                }
                System.out.println(c + " " + tmp);
            }
        }
    

    输出结果:


    uft-8编码

    gb2312编码,结果如下:


    gb2312编码
    以上,本篇完结。即将写另外一篇关于IO包源码的文章。

    参考资料:
    字节字符:Java 中字节流与字符流的区别? - 胖君的回答 - 知乎
    https://www.zhihu.com/question/39262026/answer/127103286

    IO流:https://blog.csdn.net/lmb55/article/details/79511007
    编码解码:https://blog.csdn.net/mazhimazh/article/details/19327421
    阮一峰老师关于编码解码的文章:http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html

    相关文章

      网友评论

          本文标题:Java IO流与编码解码

          本文链接:https://www.haomeiwen.com/subject/rgsfectx.html