现在java入门大家基本上都是在说springmvc,mybatis,springboot,spring cloud 等等的应用框架,说起来一套一套的,似乎很高端的样子,对方听起来一愣一愣的。但对底层的数据结构、编码、算法等这类有就所偏弱了。本人也有同样问题,我是以java技术出身,没有正统的学习过汇编、C这些比较底层的言语。对一基很基础的知识理论并不理解和透彻。之前对编码知识这块都是一知半解。所以近两天专门查阅了一些基础知识,对ascii、unicode、lantin-1、utf-8、utf-16/32编码知识补习了一下。
趁人未老,还没把知识忘记,现在归纳总结下自己的理解。本文章节包括如下几点:
- 字节基本知识
- ASCII编码
- UNICODE编码
- UTF-8编码
- UTF-16/32编码
- LANTIN-1编码
一、字节基本知识
理解ascii,unicode等编码,为什么先理解字节的基本知识了?是因为了解基本知识后有助于理解相关编码知识。 甩个百度百科链接自己看 https://baike.baidu.com/item/%E4%BD%8D%E3%80%81%E5%AD%97%E8%8A%82%E3%80%81%E5%AD%97/15650262
最终要知道是 1字节 = 8位 。举例这里有8个数字(0或1)组成的就是1字节 ,如 00000010 。 而1字节能表示的数值范围是 0 ~ 255,计算方法是 “2的8次方 ”。
二、ASCII编码
- 计算机是个文盲,只认识0和1。如果我们把 001010100010100100101010 输进计算机,它马上能知道这个数字转成10进制是9527(这个9527是瞎写的)。但是我们农民百姓怎么知道00101010001010010010 1010这个是什么鬼。所以呢,歪果仁就制定了一套规则,把字母、数字,符号都编个号,比如大写'A'是65,数字’1‘是49,转成二进制就是 65 = 0100 0001, 49 = 0011 0001 。
- 歪果仁所用的英语,也就ABC + 123 这么几个字组成,所以当时只定了127个字符对应关系,也就是ASCII编码。127个字符的是基础ascii编码,也就是最早的编码规范了。引用百度百科(https://baike.baidu.com/item/ASCII/309296?fr=aladdin)的一句话: ASCII 码使用指定的7 位或8 位二进制数组合来表示128 或256 种可能的字符。标准ASCII 码也叫基础ASCII码,使用7 位二进制数(剩下的1位二进制为0)来表示所有的大写和小写字母,数字0 到9、标点符号,以及在美式英语中使用的特殊控制字符
基础ascii码的字节表示方式是 : 0XXX XXXX (见上述百度百科描述)。 第一位必须为0,剩下的7位二进制范围是0 ~ 127。 一个字节的范围是0 ~ 255,现在基础ASCII码用到了0 ~ 127,而-1~ -128 范围干嘛用了呢?还是百度百科的一句话(如果描述不对,就找百度说去吧):
高位128个称为扩展ASCII码。许多基于x86的系统都支持使用扩展(或“高”)ASCII。扩展ASCII 码允许将每个字符的第8 位用于确定附加的128 个特殊符号字符、外来语字母和图形符号 基础ascii和扩展ascii的分布情况用图表示:
小结: 1. ascii是单字节,在 0 ~ 127 范围是基础码,在128 ~255 是扩展码; 2. 在0 ~ 127范围内是全球标准,所有单字节编码(latin-1),或长度可变编码(utf-8)都兼容。而 -1 ~ -128 范围则不兼容,每种编码规范所定义的标准都不同;
二、UNICODE规范(这里没用‘编码’二字)
歪果仁图样图森破,制定ASCII编码时只考虑了ABC,但没有考虑到非英文国家的文字,所以光有ascii码还不够,这就引出了其他编码的诞生。还是引用百度百科来解释UNICODE的诞生原因(https://baike.baidu.com/item/Unicode/750500?fr=aladdin)
因为计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理。最早的计算机在设计时采用8个比特(bit)作为一个字节(byte),所以,一个字节能表示的最大的整数就是255(二进制11111111=十进制255),0 - 255被用来表示大小写英文字母、数字和一些符号,这个编码表被称为ASCII编码,比如大写字母A的编码是65,小写字母z的编码是122。 如果要表示中文,显然一个字节是不够的,至少需要两个字节,而且还不能和ASCII编码冲突,所以,中国制定了GB2312编码,用来把中文编进去。 类似的,日文和韩文等其他语言也有这个问题。为了统一所有文字的编码,Unicode应运而生。Unicode把所有语言都统一到一套编码里,这样就不会再有乱码问题了。
目前,我没有了解UNICODE编码如何去设计每个字符的编码规则,所以需要大家自行搜索。
百科 https://baike.baidu.com/item/Unicode/750500?fr=aladdin
这里简单归纳一下我对UNICODE的理解:
- 它为每种语言中的每个字符设定了统一并且唯一的二进制编码;
- 它只是一个规范,并没有告诉大家如何存储(这点是关键)。而UTF-8,UTF-16,UTF-32是实现存储UNICODE编码的三种方式。
而有些细心的朋友会问:为什么WINDOWS下文本编辑器‘另存为’时(下图),可以选择UNICODE编码呢,不是说UNICODE是一个规范吗?
这个问题,我当时也有同样的疑问。老夫花了一些时间去搜索答案,自己也验证了一下
WINDOWS文本编辑器的‘另存为’时的Unicode 就是 UTF-16 LE 编码;Unicode big endian 就是UTF-16 BE 编码。
用windows记事本‘另存为’UNICODE编码,用emeditor打开,显示的是UTF-16 LE。
小结: 1. UNICODE是一种规范,为全球定义了每一个字符的唯一的二进制编号,但没有告诉大家如何去存储这个编号。
三、UTF-8编码
UNICODE规范的实现方式常见有三种:UTF-8、UTF-16、UTF-32。先说UTF-8吧。 UTF-8:UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码,由Ken Thompson于1992年创建,现在已经标准化为RFC 3629,UTF-8用1到4个字节编码Unicode字,是可变长度的编码方式。
见上图百度百科中的UTF-8转换表,图中可以看出在不同范围内UNICODE的字符,UTF-8所存储的字节数都不同,比如 0000 ~ 007F ,只需要 1 Byte,0080 ~ 07FF 需要2 Byte。 有些字符是一个字节,有些字符是两个字节,有些字符是三个字节。如果假如有三个字节或更多 XXXXXXXX|XXXXXXXX|XXXXXXXX (竖线是为了更好区分),那计算机怎么知道这三个字节是解析成 一个字符(3byte),还是一个字节(1 Byte) + 一个字节(2 Byte)呢? 以百度百科的描述:
UTF-8编码规则:如果只有一个字节则其最高二进制位为0;如果是多字节,其第一个字节从最高位开始,连续的二进制位值为1的个数决定了其编码的字节数,其余各字节均以10开头。
- 0xxxxxxx:单字节编码形式;
- 110xxxxx 10xxxxxx:双字节编码形式;
- 1110xxxx 10xxxxxx 10xxxxxx:三字节编码形式;
- 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx:四字节编码形式;
数前面有几个“1”,如果“0”开头,则是单字节; 如果有两个“1”,则是双字节,后面一个字节必须是“10”开头。如下图,相同颜色的为同一字符。
小结: 1. UTF-8 是UNICODE的实现存储方式之一; 2. UTF-8 是长度可变的编码,是现互联网上用的最普遍的编码;
四、UTF-16编码
UTF-16是UNICODE编码实现方式之一,该编码长度既是可变,亦是固定:采用存储的字节长度,要么是2Byte ,要么是4Byte。
UTF-16编码分为 UTF-16 little endian(LE) 和 UTF-16 big endian (BE): 1. Little endian:将低序字节存储在起始地址 2. Big endian:将高序字节存储在起始地址 这里涉及了一个概念,就是字节序,看百度百科的描述:(https://baike.baidu.com/item/%E5%AD%97%E8%8A%82%E5%BA%8F):
字节序,即字节在电脑中存放时的序列与输入(输出)时的序列是先到的在前还是后到的在前。 BIG-ENDIAN、LITTLE-ENDIAN跟CPU有关,每一种CPU不是BIG-ENDIAN就是LITTLE-ENDIAN。IA架构(Intel、AMD)的CPU中是Little-Endian,而PowerPC 、SPARC和Motorola处理器是Big-Endian。这其实就是所谓的主机字节序。而网络字节序是指数据在网络上传输时是大头还是小头的,在Internet的网络字节序是BIG-ENDIAN。所谓的JAVA字节序指的是在JAVA虚拟机中多字节类型数据的存放顺序,JAVA字节序也是BIG-ENDIAN。
查找了一些相关文章或技术贴,有一段写的相对比较容易理解的我就直接截图,不再重复描述说明,内容源于 https://blog.csdn.net/li123128/article/details/80709027
小结: 1. UTF-16 是UNICODE的实现存储方式之一; 2. UTF-16 为分little endian 和 big endian 两种方式;windows 采用是 utf-16 le ,而 mac 采用是 utf-16 be; 3. UTF-16 编码采用2byte 或 4byte 的字节来存储字符;
五、UTF-32编码
理解了ASCII、UNICODE、UTF-8 、UTF-16,那么UTF-32就没什么好讲的了。百度百科上描述的也很简单:
UTF-32 (或 UCS-4)是一种将Unicode字符编码的协定,对每一个Unicode码位使用恰好32位元。其它的Unicode transformation formats则使用不定长度编码。因为UTF-32对每个字符都使用4字节,就空间而言,是非常没有效率的。特别地,非基本多文种平面的字符在大部分文件中通常很罕见,以致于它们通常被认为不存在占用空间大小的讨论,使得UTF-32通常会是其它编码的二到四倍。虽然每一个码位使用固定长定的字节看似方便,它并不如其它Unicode编码使用得广泛。
小结: 1. 每个字符都采用 4byte 字节来存储,浪费存储空间;
六、Latin-1
还是百度百科的描述(https://baike.baidu.com/item/latin1/1183590?fr=aladdin):
Latin1是ISO-8859-1的别名,有些环境下写作Latin-1。ISO-8859-1编码是单字节编码,向下兼容ASCII,其编码范围是0x00-0xFF,0x00-0x7F之间完全和ASCII一致,0x80-0x9F之间是控制字符,0xA0-0xFF之间是文字符号。
小结: 1. latin-1 就是 iso-8895-1; 2. latin-1 是单字节编码; 3. 在 0 ~ 127 范围与 ascii 的一致;在128 ~ 255 则不同; 4. 因为latin-1(ISO-8859-1)编码范围使用了单字节内的所有空间,在支持ISO-8859-1的系统中传输和存储其他任何编码的字节流都不会被抛弃。
七、收尾
- 参考了网上大神们的总结和分享。https://blog.csdn.net/guxiaonuan/article/details/78678043 https://blog.csdn.net/hezh1994/article/details/78899683 https://blog.csdn.net/Deft_MKJing/article/details/79460485 https://www.cnblogs.com/tk55/p/6592673.html https://blog.csdn.net/tcf_jingfeng/article/details/80134600 https://blog.csdn.net/qq_36761831/article/details/82291166 中间还参考了其他的文章,很多都记不清了,所以就不一一贴出。
- 本文多方引用了百度百科的解析,并不是它解析或描述的很好,最主要是我认为百科给出的定义内容是相对个人网站或个人撰写的文章而言,是相对比较可信的。
- 写一编总结不易,前前后后花了不少时。一是要自己理解后再写下来;二是不能只单看某一编文章就下定论,需多方参考、对比。
- 理解了ASCII ,UNICODE 之后,再去了解中文,法文,俄文等编码会更容易理解。
如有错漏,请纠正!