Java语言基础-02-漫谈字符编码

261 阅读11分钟

这同样是学习java的一篇前置文章,意在开始聊Java中的字符类型等内容前。想先和大家聊一聊字符编码。当然,你可以完全不用看。因为看不看都不影响你学习Java本身。就像即使不在网上搜索各位老师的教学视频,你也知道如何面向对象 《编程》一样。但如果看了老师们的教学视频,显然你会对这个事情理解的更加 深入亿点点。

同样,这篇文章的目的,也就是想通过一个幽默有趣的方式,让大家更容易的理解一下那枯燥无味的编码集都是什么意思。所以,这篇文章就不分1、2、3...这样的结构了,我们以故事的形式,扯到哪就讲哪里。既然编故事,那么文字就不一定严谨,大家通过此文知道字符编码是怎么回事以及在后续的开发中如何用就好了。千万别钻牛角尖。

那么,我们发车吧!

我们知道,计算机本质就是一堆可以【开合】的电路(这句话不严谨,大家别较真)。每一个开合的电路元件都有两个状态,分别是【高电压】和【低电压】。分别用【1】和【0】表示。于是,天才们就用无数个不同组合的【010101011010】建立起了庞大的计算机帝国。那么问题来了。你在看这篇文章的时候,为啥看到的是文字,而不是以下这种形式:

1110101010101010010100000000001111101010101010101010100000000000000000000000101010101000000000000000000101001011111111101010101010101010111111111111111111110000000000011111101010101010000000010101010101010100000000

因为你是正常人,因为我也是正常人,两个正常人对话不可能用非人类的二级制去沟通的。可我们说了,计算机只认识二进制呀,怎么办?

有这样一群人,他们决定,那就把二进制分组吧。8位一组,每一组表达一个自然语言中的“字符”。那么,8位能够表达多少种可能呢,2的8次方256种。然后将自然字符和这些二进制的组合一一对应起来。如下图:

解释一下这张表:

表内大体表示的就是如下对应关系:八位二进制 =》 十进制 =》字符。举例说明:

  • 二进制0000 0000翻译成十进制 是0 表示的字符是 空字符

  • 二进制0000 0001 翻译成十进制 是1 表示的字符是 头标开始

  • 二进制0011 0000 翻译成十进 是48 表示的字符是 字符0

问题来了,不是说有256个么,可这张表里才127个字符呀。诶!对于美国人民来说,127个完全够用了。再往下编编不下去了,实在没那么多独立字符啊。那就这样吧。浪费就浪费了。后来,他们给这套方案取了个名字,ASCII编码。在当时,所有计算机都用这套方案来表示英文的。

时间荏苒,岁月如歌。转眼间,来到了xxxx年。这一年的具体年份记不清了,但此时的世界已经不再是彼时的那个世界了。以前计算机连美国都屈指可数,现在计算机遍布全世界。问题也就此产生了。

据说当时情况是这样的:

计算机:HiMy friend。Are you OK?

你:Can you speak chinese?

计算机:pardon?

你想想,计算机不会说中文!!这怎么能忍?而且不光中国人民不能忍,印度人民也不能忍呀,战斗民族能忍么?金元帅能忍么?八嘎呀路!就连南极的🐧也表示抗议。

这可如何是好,得让计算机多学几门语言才行啊。

没关系,没关系。幸好当初英文字符只用了127个。ASCII后面还有128到255没用呢,大家用起来吧。于是,各种新的符号便加入了进来。这一部分就成为扩展字符集,很好理解对吧。可是,问题解决了么?远远没有。256个字符就够了?太天真了!!!

中国人:This is your water.

计算机:What's your problem?

中华文明博大精深,别说剩下的这一百多个空位了,你就算把整个ASCII都给我,也容不下我泱泱大国的文字的万分之一呀。但是我们没钻牛角尖,一套ASCII不够,那就来两套呗。大概意思就是这样:

我们准备了两个字节,一个称谓高字节,一个称谓低字节。每个字节中前127依然是ASCII码。我不动。但是,一旦出现了两个高于127的字节连在一起,我们就让计算机把它翻译成汉字。 明白啥意思了么? 进一步解释:

e.g:
0110000101100001 
十进制97 97  
这是两个低于127的编码,那翻译过来就是aa

1110111011101110 
十进制238 238 
这是两个高于127的编码连在一起,那它俩就一起表示一个中文字符
那翻译过来就是 "牛"。不一定是这个汉字哈

这样,我们就可以组合出成千上万个汉字了。在这套编码里,我们编进去了各种各样的符号,甚至包括一切国外的符号也加入了进去。这套方案就叫做GB2312编码集。

你印象中是否隐约记得,好像有过那么一段时间,一个汉字要用两个字符长度呢?

再后来,这套编码方案又发展出了各种版本,其他国家也纷纷推出了自己的编码方案。方案太多了,计算机懵逼了,所以会经常出现“乱码”的情况。为什么,你这套方案可能这台计算机里没安装,它不认识啊!

话说天下大势...

这么混乱的编码方案,在一个国际化的计算机身上存在,怎么能忍!考虑过南极🐧的感受么?于是,国际化标准组织决定解决这个问题。废除了所有地区性编码方案,重新制定了一套包括了地球上所有文化、所有国家、所有字母和符号的编码方案:Unicode。

简单的理解,Unicode编码方案,和之前的编码方案思路也没有差别,就是用更多的字节,表示更多的字符而已。这里就不像ASCII一样,把整个Unicode的编码方案截图放出来了,大家理解思路就好了。

比如汉字的“汉”,Unicode编码是多少?是0110110001001001,十六进制就是:0x6C49。

从此,所有计算机都要支持Unicode编码,底层一看到 0110110001001001 就知道,这个是 “汉子”。不不不,是“汉”字

但是请注意,编码仅仅是一个符号集,Unicode虽然规定了某个符号的二进制代码,却没有规定该怎么存储这个代码。比如上面的“汉”字,是16位二进制,至少需要两个字节来保存。

但是如果存储一个英文字符,一个字节就够了。

可能还有其他的字符需要三个字节。这不就又乱了。

到底要几个字节存储一个字符啊,

有的人可能要说了,那就强制规定所有的字符都用10个字节存储算了,这样别说表示🐧,就算以后外星人来了也够了。何必那么小气?

这不是不可以,但确是对存储资源和传输效率极大的浪费,不妥!

举个栗子:

如果在你面前出现3个人:你一看就知道,这叫:"三口之家"

如果在你面前出现2个人:你一看就知道,这叫:"情侣"

如果在你面前出现1个人:你一看就知道,这叫:"单身狗"

为什么你一看就会知道呢?因为在你心中有一套认知方案:这个方案就是Unicode

但是,无论是“三口之家”,还是“单身狗”,他们都要居住在某个地方对吧,不可能睡大街啊。于是,他们就要去租房子了。

如果你是包租公,你家有十栋楼对外出租,你觉得怎么规划你家的【十栋楼】最划算呢?

难道为了兼容所有情况,你把你家的十栋楼,全部装修成三室一厅么?这样不就能兼容所有情况了么?但是不是有很大的优化空间呢?

如果能动态的提供一居室,二居室,三居室。比如见到“单身狗”,就给他个一居室。见到"情侣"就给他们一个两居室。这样是不是更好呢?


对!Unicode也面临这个问题?

于是,UTF-8应运而生。

它是一种变长的编码方式,根据情况不同,可以使用1~4个字节来存储一个字符。

对于单字节符号,字节的第一位设为0,后面的七位为这个符号的Unicode码。所以对于英文字符而言,UTF-8编码和ASCII码是相同的。

那对于多字节的字符呢,比如“汉”字。规定第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。

我是谁,我在哪,我在干啥?上面这段话在说啥?举例说明吧:

"汉"Unicode编码 01101100 01001001
大家觉得这应该是两个字节存储对吧,先别着急,UTF-8会告诉你真正用几个字节存储:

Unicode符号范围(16进制)  |Unicode符号范围(10进制)|UTF-8编码方式         
------------------------------------------------------------------------------
0000 0000 ~ 0000 007F  | 0-127	             |0xxxxxxx
0000 0080 ~ 0000 07FF  | 128-2047            |110xxxxx 10xxxxxx
0000 0800 ~ 0000 FFFF  | 2048-65535          |1110xxxx 10xxxxxx 10xxxxxx
0001 0000 ~ 0010 FFFF  | 65536-1114111       |11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

我们的”汉“字 转换成10进制是 27721  属于第三行,占3个字节,那么,其UTF-8编码格式就是
1110xxxx 10xxxxxx 10xxxxxx
xxxxx填写上对应的unicode码
1110(0110) 10(110001) 10(001001) 
最终:"汉"字的UTF-8码为:11100110 10110001 10001001

至此,大家知道Unicode和UTF-8是真么回事了吧,Unicode是一个理论上的编码集。对所有字符编码做了统一规定,而UTF-8是实际上存储和使用这些编码的存储规范。

对应的,还有UTF-16,UTF-32。都是对Unicode编码的使用方式而已。

至此,我们关于编码的故事就讲完了,今后的学习中,你会慢慢的进一步了解的,这里通过这篇文章,加深一下你对Unicode编码和UTF-8的印象即可,因为未来的学习工作中,你可能会经常看到这两个概念。