现在java入门大家基本上都是在说springmvc,mybatis,springboot,spring cloud 等等的应用框架,说起来一套一套的,似乎很高端的样子,对方听起来一愣一愣的。但对底层的数据结构、编码、算法等这类有就所偏弱了。本人也有同样问题,我是以java技术出身,没有正统的学习过汇编、C这些比较底层的言语。对一基很基础的知识理论并不理解和透彻。之前对编码知识这块都是一知半解。所以近两天专门查阅了一些基础知识,对ascii、unicode、lantin-1、utf-8、utf-16/32编码知识补习了一下。
理解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码的字节表示方式是 : 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 范围则不兼容,每种编码规范所定义的标准都不同;
歪果仁图样图森破,制定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
而有些细心的朋友会问:为什么WINDOWS下文本编辑器‘另存为’时(下图),可以选择UNICODE编码呢,不是说UNICODE是一个规范吗?
这个问题,我当时也有同样的疑问。老夫花了一些时间去搜索答案,自己也验证了一下
用windows记事本‘另存为’UNICODE编码,用emeditor打开,显示的是UTF-16 LE。
小结: 1. UNICODE是一种规范,为全球定义了每一个字符的唯一的二进制编号,但没有告诉大家如何去存储这个编号。
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开头。
数前面有几个“1”,如果“0”开头,则是单字节; 如果有两个“1”,则是双字节,后面一个字节必须是“10”开头。如下图,相同颜色的为同一字符。
小结: 1. UTF-8 是UNICODE的实现存储方式之一; 2. UTF-8 是长度可变的编码,是现互联网上用的最普遍的编码;
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 的字节来存储字符;
理解了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 字节来存储,浪费存储空间;
还是百度百科的描述(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的系统中传输和存储其他任何编码的字节流都不会被抛弃。
如有错漏,请纠正!