Java IO流与编码解码

2019-08-24  本文已影响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流有:

两者之间的转换是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

上一篇下一篇

猜你喜欢

热点阅读