浅记「字节/字符流」乱码问题

字符编码

Unicode 统一了所有字符的编码,包括中文,它算是一个字符集,用 2 字节大小对所有字符进行了唯一编码

但是 Unicode 没有规定如何存储,UTF-8 刚好规定了 Unicode 字符如何存储的问题。UTF-8 采用可变长编码,英文用 1 个字节,中文用 3 个字节

对于 UTF-8 编码中的任意字节 B:

举个简单的例子,中文汉字「」对应的 Unicode 编码:十进制: 25105;十六进制:0x6211;二进制:01100010 00010001

它在 UTF-8 编码下的存储结构为:十六进制:0xE68891;二进制:11100110 10001000 10010001

为了方便对比,写到一起:

乱码分析

在学习字节流和字符流的时候,遇到过一个问题:为什么 I/O 流操作要分为字节流操作和字符流操作呢?

本篇文章就上面的第二点展开讨论为什么会出现乱码问题!!!

如果我们用字节流去读 UTF-8 编码的文件,那么每次只能一个字节一个字节的读。对于一个中文汉字需要读三次,每次读的结果如下:11100110、10001000、10010001

可以看到三次读出来的结果其实就是上面分析过的 UTF-8 编码下的存储结构,此时就会将每一次的结果转化成一个 ACSII 字符:

所以乱码就出现了!!另外可以从下面程序中得到验证:

如果我们改用字符输入流,它每次都读一个字符,而且已经自动处理了编码问题,具体可见下面程序:

input.txt文件中存储的结构是:11100110 10001000 10010001,不信的话可以用二进制文件查看器打开它

但程序输出的二进制内容不再是 UTF-8 编码下存储的内容,而是对其进行了解码,转换成了 Unicode 下的编码,所以最后才没有乱码

注意:字符输入流并非每次都读三个字节。如果一个字符只用一个字节存储,那么一次就读一个字节,如果一个字符用三个字节存储,那么一次就读三个字节,可以根据 UTF-8 编码判断每次读几个字节

总结:字节输入流每次读一个字节,字符输入流每次读一个字符,而且字符输入流还自动的对 UTF-8 解码成 Unicode