Java 字节流与字符流的字符集编码及乱码(1)--对比

目录

Java 的 IO 系统是比较庞杂的, 各种流特别多, 其中有一种就是字符流.

在本系列前面的一些文章中, 也曾涉及过字符流的话题, 不过没有详细展开讨论, 这次准备具体综合地谈一谈.

在层次方面的对比

你可能听过不少关于字节流与字符流对比的介绍, 不过严格地说, 我认为把"字节流"和"字符流"去对比这种说法不是特别妥当, 为什么呢?

首先, 这两种流实际上处在不同的层次, 字节流是基础, 而字符流是构建在其上的:

字节流 字符流 层级关系

对于不同层次上的事物, 我认为用"对比"这个词是不太恰当的.

打个不太恰当的比方, 比如说你跟隔壁老王同龄, 然后你"拿你的儿子跟隔壁老王对比", 那隔壁老王可能就会质问你: "你什么意思? 拿你儿子跟我比? 你咋不拿你爹跟我比呢? "

你可以"拿自己跟老王对比", 也可以"拿自己儿子跟老王儿子对比", 但你不能"拿你的儿子跟隔壁老王对比", 这就不恰当了, 对吧?

要是一对比, 发现儿子长得还挺像隔壁老王, 那就尴尬了...

那么这里是想说明一个道理, 就是不是一个辈分的, 不应该去对比.

再打一个比方, 就是好比你把"中国"去跟"美国的加利福尼亚州"去对比, 通常意义都不大, 我们一般都不会这样去做.

但在某些特殊情况下, 比如想说明加州的经济特别发达, 可以拿加州的 GDP 跟整个中国的 GDP 对比, 但这种对比只是为了突出一方而不是为了对比双方.

那么同理, 处在不同层次的字节流与字符流, 它们也不适合去"对比". 但是, 可以研究"字节流"和"字符流"之间的关系, 这是没有问题的. 那么它们之间到底什么关系呢? 其实前面那个图也揭示地很清楚了:

字节流是基础, 字符流是其上的抽象与封装.

在文件层面的对比

狭隘地讲, 比如仅仅从读取或写入文件方面去探讨的话, 那么"字符流"是为方便我们读取或写入"文本文件"而引入的抽象. 也就是说:

字符流只对应文本文件;

而字节流则对应所有文件, 自然也包括文本文件.

文本文件是开发活动中会大量接触到的一类文件. 所有语言的源代码文件, 像什么 .java, .js 这些都是;还有很多比如 html, xml, css 之类的以及很多的配置文件也是文本文件.

简单讲, 就是你可以用"记事本"打开查看的那类文件.

假如你一个文本文件要读取, 自然, 最好的选择是使用字符流. 当然, 你也可以选择用字节流来读取. (在后面, 会给出一些具体的例子和代码)

如果只要一下子就把整个文件读取上来, 那么用字符流跟字节流的差别并不大.

但是, 面临某些具体的需求时, 比方说, 想一个一个"字符"的读取上来, 或者想一行一行地读取时, 用字符流就会很方便, 而用字节流就会非常的麻烦.

另一方面, 如果你要读取的并不是一个文本文件, 那就不能用字符流了. 比如说, 读取一个图片文件, 或者是一个压缩包, 又或者是一个 word 文件或 pdf 文件. 这些都不是"文本文件", 因此你不可以用字符流去读取它们.

简单讲, 就是你不可以用"记事本"打开去查看它们的那类文件. 当然, 如果你硬是要用记事本打开也不是不可以, 毕竟在最底层, 大家都不过是一堆 0 和 1 而已, 但这种强行打开通常只会呈现为一堆乱七八糟的东西.

非文本文件不可以用字符流去读取, 但它们都可以用字节流去读取, 因为本质上来讲, 任何的文件都不过是字节的序列而已.

我看到有些介绍字节流与字符流的文章没有特别去强调这一点, 给人一种感觉, 就好像这两种流都能做任何事情, 这显然是错误的.

其实从名字上也不难想到, 既然叫字符流, 那肯定跟字符有关了. 而对于图片来说, 构成元素是一个个"像素", 而不是什么字符, 所以自然不能用字符流去读取图片. 初学者或许能意识到不能用字符流去读图片, 但未必能清楚明白, 其实还有很多的文件都不能用字符流去读取的.

所以, 前面强调两者不能对比, 强调两者处在不同层次, 在能力上, 两者也是有区别的, 而所有的字符流最终它的底层其实还是字节流, 字符流只是一种抽象. (甚至还不算是一个好的抽象, 我们将在后面说明为什么)

在字符集编码方面的区别

对于构建一个字符流而言, 其实有一个很重要的参数, 却经常被大家所忽略, 就是字符集编码.

为什么你可以忽略它呢? 因为有缺省的存在. 就好像你用记事本保存一个文本文件时, 系统通常只提示你输入文件名, 而不会提示你选择一个字符集编码, 但这并不是说编码是可有可无的, 当你没有选择编码时, 其实是系统悄悄地给你分配了一个缺省编码.

某些时候, 这带来了方便, 但也常常给我们带来很多问题.

其实在前面的图中, 我把字符流写成 "gbk 字符流", "utf-8 字符流"也是为了凸显这个编码的存在, 它不应该被忽略.

前面说, 字符流构建在字节流基础之上, 其实更加确切的说法应该是: 字符流构建在 字节流+字符集编码 基础之上, 像这样:

字符流 字节流 字符集编码 关系

最后, 字符流中所谓的"字符"其实跟我们认知还是有区别的, 前面也说了, 这里的字符其实并不是一个很好的抽象.

这里的"字符"严格说, 只是 BMP 内的字符, 对于 BMP 外的, 情况就不同了.

在下一篇, 再给出具体的示例.