1 import java.io.IOException; 2 import java.util.Arrays; 3 4 import org.junit.Test; 5 6 public class Unicode_Test { 7 /* 8 byte[] bytes = str.getBytes("编码格式") 【按照指定格式编码】----> 9 fos.write(bytes,0,length)【根据每个字节对应的编码数字找到计算机中存储的编号,编号就是我们所说的字节流】【数字转字节流】----> 10 把字节流存到内存中--------> 11 比如说我们的Eclipse软件,环境编码格式是utf-8,那他的解码格式也是默认utf—8,fis.read("定义一个字节数组,用来装读取到的内存中每个编号对应的数字")【字节流流转数字】 12 【我们视觉看到的文本,是通过我们使用的软件根据自己的解码方式把计算机中存储的编号对应的数字进行解码。】-----> 13 通过String newStr =new String("要转化成字符串的字节数组","要用的解码格式"); 14 */ 15 16 @Test 17 public void test() throws IOException { 18 String str = new String("abc中国"); 19 byte[] bytes = str.getBytes("gbk"); // 按照gbk格式对字节进行编码,没有就默认环境编码格式 [97, 98, 99, -42, -48, -71, -6] 20 byte[] bytes2 = str.getBytes("utf8");// 按照utf8格式进行编码,[97, 98, 99, -28, -72, -83, -27, -101, -67] 21 System.out.println(Arrays.toString(bytes)); 22 System.out.println(Arrays.toString(bytes2)); 23 System.out.println(new String(bytes)); // 按照默认utf—8进行解码 abc�й� 24 System.out.println(new String(bytes, "gbk")); // 按照编码格式进行解码 abc中国 25 } 26 }
一.编码
人类先有了自己的语言,交流了若干个世纪,然后出现了计算机。可惜计算机只认0和1,人类只能认文字,双方都不能妥协,那就必须要有一个从文字到0、1的映射了。从文字到0、1的映射称为编码,反过来从0、1到文字叫解码。
具体什么 是编码?先来咬文嚼字一下吧。编就是将某样东西按照一定的规则放到一起,码在这里是数字的意思。编码就是将某东西编成数字。比如邮政编码,就是将不同范围 内的邮局编成不同的数字。计算机里只有0和1,编码就是将文本字符编成一系列的0和1,看起来好像是废话啊,但这确实是编码的本质。
后来经过编码,计算机屏幕上终于可以显示“Hello World”了。学计算机之前谁都不知道有这么一个过程,因为一切看上去都理所当然。这种根深蒂固的认识让我们对编码理解起来犯了难。
首先屏幕 的显示跟计算机存储是两码事,屏幕对应人们的视觉认知,它是无形的,你找不出来在哪里刻了这么两个单词,而计算机存储是客观存在的。计算机里只有0和1, 怎么来表示“Hello World”呢(假如我们在美国),那就需要将字母数字及标点符号编一个号。一个字节可以表示256个数字,表示字母数字标点足够了,所以用一个字节就可以对应一个字符了。这样一来计算机在显示文字的时候,先将0、1解码成对应的文字,然后在屏幕上渲染出来就可以了。我们将“Hello World”叫做字符,计算机实际存储的是字符对应的编号,这些编号就叫字节流。
上边这种编码就是ASCII码,如果计 算机只在美国用或者只显示英语,那编码就是透明的,谁都不需要去关心编码,一切都觉得理所当然。可是计算机应用到了像中国这样的国家,这些国家的语言哪里 只是几个字母啊,有成千上万种不同的字符。很显然ASCII码就不能满足需求了,怎么办呢,每个国家都研制自己的编码呗,很显然这样做并不长久,每个国家 都有自己的编码实在有点乱,连两个国家的语言都不能放在一起。所以可以将世界当成一个整体,把所有的文字统一编号,这时候就出现了unicode编码。用 一个字节来表示一个字符显然是不够的,unicode编码用了两个字节来表示一个字符。其实,编码的发展过程并没有这么顺利,中间还是出现了很多其他的编 码,以后的文章可以详细说一下几种常用的编码。那问题岂不是解决了,大家都用unicode不就完事了吗,哪有这么简单呢,unicode出现之前计算机 领域已经有很多成型的操作系统软件甚至标准,不可能都统一改成unicode编码。所以到现在还是会遇到编码问题,unicode只是给我们提供了一种统 一解释所有文字的编码方案。要搞清楚,这里讨论的编码都是针对文本字符的。
二.乱码
编码之所以受到关注,乱码几乎起到了决定性的作用,如果没有乱码,一切都让大家觉得顺理成章,那谁还会关注编码呢。
出现乱码的原因就是文本字符编码过程与字节流解码过程使用了不同的编码格式,这个往往归咎于解码格式选择错误,也就是说在解码的过程中出现了问题。如 果我的字符是用utf-8编码,你用GBK解码那肯定出问题。因为文字按照utf-8的编码规则编成的0、1,按照GBK的规则解码回来的文字并不是原来 的文字,这时候就会出现乱码了。这种问题会出现在文件读写、网络编码传输、数据库存取上。只要牵涉到字符都有可能出现乱码,因为只要有字符就会有解码过 程。
还有一种 情况就是文件压根不是文本文件,也就是说根本就没有经过编码这个过程,那你去解码当然乱码了。比如64,你如果看做文本字符就是6和4两个字符,可以对应 编码格式进行编码。如果看做是数字64,那对应的存储结构是01000000,就没有编码过程,也就不需要去解码。
要 搞清楚的一点就是同样的文本字符,经过不同的编码,在存储结构上是不一样的,但是代表的字符是一样的,不同编码真正的区别在于存储结构。反过来,相同的存 储结构,经过不同的解码,对应的文本字符并不一样,但是在内存上结构上并没有改变。如果碰到乱码,不要慌张,因为原始存储结构一动没动,只不过用错了解码 方式。就像一千个读者有一千个哈姆雷特一样,真实的哈姆雷特就在那里。
乱码是显示在屏幕上才被认为是乱码,也就是说乱码取决于人的感官,乱码只有人才知道﹐计算机不认为这是乱码。
三.文件编码
不管是文本还是图片或视频,在计算机存储上都是一视同仁,全都是字节流。但是 从方便人们阅读的角度上还是分为文本文件和二进制文件。文本文件的可视形式就是文本字符,在存储和显示时有文本字符编解码的过程,可以直接用文本编辑器阅 读。除文本文件以外就是二进制文件,不同类型的二进制文件都有相应的结构标准,例如java的class文件,前四个字节代表文件类型,后边两个字节代表 大版本号,再后边两个字节代表小版本号。具体哪些字节代表什么意思,值是float类型还是int类型,都有一定的标准,所以需要特定的软件按照标准去读 取解析。
在不同的编程语言中,往往提供不同的类对文本文件和二进制文件进行读写。最常 用的就是文本文件的读写例如C#中有StreamReader和StreamWriter,Java中有BufferedReader和 BufferedWriter。还有二进制文件的读写例如C#中有BinaryReader和BinaryWriter,Java中有 DataInputStream和DataOutputStream。当然读写二进制文件的类也可以读写文本文件,因为文本文件和二进制文件的存储在本质 上是没有区别的,都是二进制。只不过专门读写文本文件的类封装的更好,读写文本文件更方便。