ASCII、Unicode、UTF8 10年后,重新整理《专题1》
以后基本都是Unicode定义二进制对应的字符,UTF8来按照一种格式来存储Unicode二进制,所以只需知道 unicode
unicode 官网:https://home.unicode.org/ 包含了 全世界的所有符号,各种“emoji”表情也包括在内;
ASCII
计算机内部的数据最终以二进制形式存放,每个二进制位(bit)有0和1两种状态,而8个二进制位(bit)组合称为一个字节(Byte),所以一个字节能够组合出256中状态,即从00000000到11111111。
上世纪70年代,美国国度标准协会(American National Standard Institute , ANSI )制订了ASCII码(American Standard Code for Information Interchange,美国标准信息交换码):使用7 位二进制数共128个组合来表示所有的大写和小写字母,数字0 到9、标点符号, 以及在美式英语中使用的特殊控制字符。
第0~32号及第127号(共34个)是控制字符或通讯专用字符,如控制符:LF(换行)、CR(回车)、FF(换页)、DEL(删除)、BEL(振铃)等;
第33~126号(共94个)是字符,其中第48~57号为0~9十个阿拉伯数字;65~90号为26个大写英文字母,97~122号为26个小写英文字母,其余为一些标点符号、运算符号等。
3、UNICODE
一个字节中的后7位总共只能表示128个不同的字符,英语用这些字符已经足够了,可是要表示其他语言却是不够。比如,在法语中,字母上方有注音的符号,就无法用ASCII表示。于是,一些国家就利用了字节中闲置的最高位编入新的符号。这样一来,就可以表示最多256个符号,这就是扩展的ASCII 码,所以现在有7位和8位的两种ASCII码,扩展的ASCII 码允许将每个字符的第8 位用于确定附加的128 个特殊符号字符、外来语字母和图形符号。但是,不管怎样,0~127表示的字符是一样的,不同的只是128~255.
PS:查询后128个ASCII对应字符的技巧:新建一个文本文档,按住ALT+要查询的码值(注意,这里是十进制) ,松开即可显示出对应字符。
但即使扩展到256个符号也不够用,比如汉字据统计有10万个以上,而且同一个数值在各国的语言中表示的却不同,比如130在法语里面é,而在希腊语里面则代表Gimel。于是UNICODE应运而生。
Unicode字符集编码是Universal Multiple-Octet Coded Character Set 通用多八位编码字符集的简称,是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。
Unicode是一种在计算机上使用的字符编码。它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。Unicode 标准始终使用十六进制数字,而且在书写时在前面加上前缀“U+”,例如字母“A”的编码为 004116 和字符“?”的编码为 20AC16。所以“A”的编码书写为“U+0041”。
但Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。这句话的意思是,如何存储才能被计算机程序直观的知道,举例:
字符 “中” ,存在内存中,若是直接使用unicode规定的 二进制 来存,那么需要 2个字节,但是计算机程序在读取的时候是一个字节,一个字节读取的,并不知道 这2个字节组成了一个 unicode字符,
所以需要一套规则,来存储,读取的时候 再根据存储的规则 就知道 原来这2个字节 是连在一起 组成一个字符的。
Unicode的问题
需要注意的是,Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。
比如,汉字"严"的unicode是十六进制数4E25,转换成二进制数足足有15位(100111000100101),也就是说这个符号的表示至少需要2个字节。表示其他更大的符号,可能需要3个字节或者4个字节,甚至更多。
这里就有两个严重的问题,第一个问题是,如何才能区别Unicode和ASCII?计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?第二个问题是,我们已经知道,英文字母只用一个字节表示就够了,如果Unicode统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储来说是极大的浪费,文本文件的大小会因此大出二三倍,这是无法接受的。
它们造成的结果是:1)出现了Unicode的多种存储方式,也就是说有许多种不同的二进制格式,可以用来表示Unicode。2)Unicode在很长一段时间内无法推广,直到互联网的出现。
UTF-8
为了解决这个问题,就出现了一些存储规则中间格式的字符集,他们被称为通用转换格式,即UTF(Universal Transformation Format)。目前存在的UTF格式有:UTF-7, UTF-7.5, UTF-8, UTF-16, 以及 UTF-32。
UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码(定长码),也是一种前缀码。它可以用来表示Unicode标准中的任何字符,且其编码中的第 一个字节仍与ASCII兼容,这使得原来处理ASCII字符的软件无须或只须做少部份修改,即可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或传 送文字的应用中,优先采用的编码。
UTF-8用1~4个字节对Unicode进行编码。从Unicode到UTF-8的编码方式如下:
unicode编码范围 | 10进制来直观观看范围 | UTF8格式来存的话,用的字节数和最终的二进制 | 10进制表示最多能多少个字符 | 描述 |
000000 - 00007F |
0 - 127 |
0xxxxxxx ,1个字节,完全与ansic编码一致,能表示西方所有符号 | 2的7次方 = 128 | 完全兼容ansic码 能表示 128个字符 |
000080 - 0007FF |
128 - 2047 |
110xxxxx 10xxxxxx,2个字节,几个x表示最多能2的几次方个字符 | 2个11次方 = 2048个字符 |
拉丁文 希腊 阿拉伯 等西方语言上 又新增少数符号的国家,意思就是说 这些国家大多数情况下,也是 使用ansic码就可,但是 这些国家 又有自己的一小部分其它符号,这些符号加起来 又不多,那么他们 就用2个字节就可。 |
000800 - 00FFFF |
2048 - 65535 |
1110xxxx 10xxxxxx 10xxxxxx,3个字节 | 2的16次方=65,536个字符 |
6万多个字符,中文一共10万个字符左右,那么6万多个字符需要被全部国家共享,所以 各个国家常用的字符,都会被归类到3个字节,大多数中文常用的都是3个字节,足以覆盖全世界 国家的常用字符了 |
010000 - 10FFFF |
65536 - 1114 111 (100多万) |
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
2的21次方 = 2,097,152, 200多万,足以覆盖全部unicode字符 |
各个国家不常用的字符,用4个字节来存储,4个字节能表示 200多万个字符,足以覆盖全世界 |
对于0x00-0x7F之间的字符,UTF-8编码与ASCII编码完全相同;
带有附加符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文及它拿字母则需要二个字节编码(Unicode范围由);
其他基本多文种平面(BMP)中的字符(这包含了大部分常用字)使用三个字节编码;
其他极少使用的Unicode 辅助平面的字符使用四字节编码;
UTF-8编码的最大长度是4个字节。从上表可以看出,4字节模板有21个x,即可以容纳21位二进制数字。Unicode的最大码位0x10FFFF也只有21位。
UTF-8解析算法:
如果字节(Byte)的第一位为0,则B为ASCII码,并且Byte独立的表示一个字符;
如果字节(Byte)的第一位为1,第二位为0,则Byte为一个非ASCII字符(该字符由多个字节表示)中的一个字节,并且不为字符的第一个字节编码;
如果字节(Byte)的前两位为1,第三位为0,则Byte为一个非ASCII字符(该字符由多个字节表示)中的第一个字节,并且该字符由两个字节表示;
如果字节(Byte)的前三位为1,第四位为0,则Byte为一个非ASCII字符(该字符由多个字节表示)中的第一个字节,并且该字符由三个字节表示;
如果字节(Byte)的前四位为1,第五位为0,则Byte为一个非ASCII字符(该字符由多个字节表示)中的第一个字节,并且该字符由四个字节表示;
总结,用UTF8这种规则格式来 转换存储unicode的话,最大能表示的字符数 为:128 + 512 + 65536 + 2097152 = 2,163,328 个
UTF-8
互联网的普及,强烈要求出现一种统一的编码方式。UTF-8就是在互联网上使用最广的一种Unicode的实现方式。其他实现方式还包括UTF-16(字符用两个字节或四个字节表示)和UTF-32(字符用四个字节表示),不过在互联网上基本不用。重复一遍,这里的关系是,UTF-8是Unicode的实现方式之一。
UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
UTF-8的编码规则很简单,只有二条:
1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。
2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。
下表总结了编码规则,字母x表示可用编码的位。
Unicode符号范围 | UTF-8编码方式
(十六进制) | (二进制)
--------------------+---------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx我猜测程序的规则:读取一个字节,判断第一个bit是0还是1,0的话 就是ansic码表,1的话,再判断第2个bit,若第2个bit是1,就再判断第3个bit.若第2个bit是0,则说明是上一个字节的一部分,直接交给程序;
以下是我自己的总结,小李飞刀:
if(first bit = 0){ //0 xxxxxxx是一个字节的ansci字符,根据后7位 得出字符 }else{ //说明第一个bit是1,因为bit要么是0,要么是1
if(second bit = 0){ //10 xxxxxx 说明这个字节是组成字符的一部分,归属上一个字节,取出 10 后的 6位,送给上一个字节就可 }else{ //走到这里说明,第一个bit是1,第二个bit也是1,然后判断第三个bit
if(third bit = 0){ //110 xxxxx 说明当前字节是2个字节 组成的字符的 首字节,取出后5位 作为连接接下来的bit的首 }else{ //走到这里说明第3个bit还是1,再判断第4个字bit
if(fourth bit = 0){ //1110 xxxx 说明当前字节是3个字节 组成的字符的 首字节,取出后4位 作为连接接下来的bit的首 }else{ //走到这里说明前4个bit都是1,再判断第5个bit
if(five bit = 0){ //11110 xxx说明当前字节是4个字节 组成的字符的 首字节,取出后3位 作为连接接下来的bit的首 }else {
//第5个bit也等于1,不存在这样的情况,放弃 } } } } }
跟据上表,解读UTF-8编码非常简单。如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。
下面,还是以汉字"严"为例,演示如何实现UTF-8编码。
已知"严"的unicode是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800-0000 FFFF),因此"严"的UTF-8编码需要三个字节,即格式是"1110xxxx 10xxxxxx 10xxxxxx"。然后,从"严"的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,"严"的UTF-8编码是"11100100 10111000 10100101",转换成十六进制就是E4B8A5。
Unicode与UTF-8之间的转换
通过上一节的例子,可以看到"严"的Unicode码是4E25,UTF-8编码是E4B8A5,两者是不一样的。它们之间的转换可以通过程序实现。
在Windows平台下,有一个最简单的转化方法,就是使用内置的记事本小程序Notepad.exe。打开文件后,点击"文件"菜单中的"另存为"命令,会跳出一个对话框,在最底部有一个"编码"的下拉条。
里面有四个选项:ANSI,Unicode,Unicode big endian 和 UTF-8。
1)ANSI是默认的编码方式。对于英文文件是ASCII编码,对于简体中文文件是GB2312编码(只针对Windows简体中文版,如果是繁体中文版会采用Big5码)。
2)Unicode编码指的是UCS-2编码方式,即直接用两个字节存入字符的Unicode码。这个选项用的little endian格式。
3)Unicode big endian编码与上一个选项相对应。我在下一节会解释little endian和big endian的涵义。
4)UTF-8编码,也就是上一节谈到的编码方法。
选择完"编码方式"后,点击"保存"按钮,文件的编码方式就立刻转换好了。
7. Little endian和Big endian
上一节已经提到,Unicode码可以采用UCS-2格式直接存储。以汉字"严"为例,Unicode码是4E25,需要用两个字节存储,一个字节是4E,另一个字节是25。存储的时候,4E在前,25在后,就是Big endian方式;25在前,4E在后,就是Little endian方式。
这两个古怪的名称来自英国作家斯威夫特的《格列佛游记》。在该书中,小人国里爆发了内战,战争起因是人们争论,吃鸡蛋时究竟是从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开。为了这件事情,前后爆发了六次战争,一个皇帝送了命,另一个皇帝丢了王位。
因此,第一个字节在前,就是"大头方式"(Big endian),第二个字节在前就是"小头方式"(Little endian)。
那么很自然的,就会出现一个问题:计算机怎么知道某一个文件到底采用哪一种方式编码?
Unicode规范中定义,每一个文件的最前面分别加入一个表示编码顺序的字符,这个字符的名字叫做"零宽度非换行空格"(ZERO WIDTH NO-BREAK SPACE),用FEFF表示。这正好是两个字节,而且FF比FE大1。
如果一个文本文件的头两个字节是FE FF,就表示该文件采用大头方式;如果头两个字节是FF FE,就表示该文件采用小头方式。
8. 实例
下面,举一个实例。
打开"记事本"程序Notepad.exe,新建一个文本文件,内容就是一个"严"字,依次采用ANSI,Unicode,Unicode big endian 和 UTF-8编码方式保存。
然后,用文本编辑软件UltraEdit中的"十六进制功能",观察该文件的内部编码方式。
1)ANSI:文件的编码就是两个字节"D1 CF",这正是"严"的GB2312编码,这也暗示GB2312是采用大头方式存储的。
2)Unicode:编码是四个字节"FF FE 25 4E",其中"FF FE"表明是小头方式存储,真正的编码是4E25。
3)Unicode big endian:编码是四个字节"FE FF 4E 25",其中"FE FF"表明是大头方式存储。
4)UTF-8:编码是六个字节"EF BB BF E4 B8 A5",前三个字节"EF BB BF"表示这是UTF-8编码,后三个"E4B8A5"就是"严"的具体编码,它的存储顺序与编码顺序是一致的。
===============================================================================
字节存储的顺序
一个字符,通过Unicode,得到了二进制流;二进制流通过转换格式,得到了几个字节。那么这几个字节是由低位到高位(即低位在前)写入硬盘呢,还是由高位到低位(即高位在前)写入硬盘呢?这就是字节存储的顺序问题,即字节序问题。
为什么不统一地都由低位到高位或由高位到低位呢?因为设备执行不同的任务时,这两种方式的性能有差别。为了最大地提升性能,设备的硬件实现根据其最常做的任务决定了字节序。
Unicode规范中用字节序标记字符(BOM)来标识字节序,它的编码是FEFF。这样如果接收者收到FEFF,就表明这个字节流是高位在前的;如果收到FFFE,就表明这个字节流是低位在前的。
这里相当于有一个从byte[]和BOM到byte[]的字节序函数(b(byte[],bom):byte[]),给定几个字节和字节序,该函数就能算出这几个字节存往硬盘的顺序。
至此,计算机屏幕上显示的字符保存到硬盘上的过程已经清晰了:
对该字符c应用编码字符集,结果为n。 |
Encoding(c) → n |
对整数n应用转换格式,结果为。 |
Encoding(n) → bytes |
对应用字节序函数,结果为硬盘上的字节数组。 |
b(bytes,bom) → bytesInDisk |
UTF-8有严格的字节顺序,不需要BOM。但如果在字节流前面加上BOM在UTF-8下的像EF BB BF,接收者一收到就知道这是UTF-8编码。所以用UTF-8格式存储,仍然会往文件头写EF BB BF,以表明其转换方法。
UTF-16、UCS-2、UCS-4、UTF-32都没有严格的顺序,所以需要借助BOM。
=============================================
1.容易产生后歧义的两字节
unicode的第一个版本是用两个字节(16bit)来表示所有字符
.实际上这么说容易让人产生歧义,我们总觉得两个字节就代表保存在计算机中时是两个字节.于是任何字符如果用unicode表示的话保存下来都占两个字节.其实这种说法是错误的.
其实Unicode涉及到两个步骤,首先是定义一个规范,给所有的字符指定一个唯一对应的数字,这完全是数学问题,可以跟计算机没半毛钱关系.第二步才是怎么把字符对应的数字保存在计算机中,这才涉及到实际在计算机中占多少字节空间.
所以我们也可以这样理解,Unicode是用0至65535之间的数字来表示所有字符.其中0至127这128个数字表示的字符仍然跟ASCII完全一样.65536是2的16次方.这是第一步.第二步就是怎么把0至65535这些数字转化成01串保存到计算机中.这肯定就有不同的保存方式了.于是出现了UTF(unicode transformation format),有UTF-8,UTF-16.
Unicode版本2
前面说的都是unicode的第一个版本.但65536显然不算太多的数字,用它来表示常用的字符是没一点问题.足够了,但如果加上很多特殊的就也不够了.于是从1996年开始又来了第二个版本.用四个字节表示所有字符.这样就出现了UTF-8,UTF16,UTF-32.原理和之前肯定是完全一样的,UTF-32就是把所有的字符都用32bit也就是4个字节来表示.然后UTF-8,UTF-16就视情况而定了.UTF-8可以选择1至8个字节中的任一个来表示.而UTF-16只能是选两字节或四字节..由于unicode版本2的原理完全是一样的,就不多说了.
前面说了要知道具体是哪种编码方式,需要判断文本开头的标志,下面是所有编码对应的开头标志
EF BB BF UTF-8
FE FF UTF-16/UCS-2, little endian
FF FE UTF-16/UCS-2, big endian
FF FE 00 00 UTF-32/UCS-4, little endian.
00 00 FE FF UTF-32/UCS-4, big-endian.
其中的UCS就是前面说的ISO制定的标准,和Unicode是完全一样的,只不过名字不一样.ucs-2对应utf-16,ucs-4对应UTF-32.UTF-8是没有对应的UCS
ASCII、Unicode、UTF8 10年后,重新整理《专题1》的更多相关文章
- 浅显总结ASCII Unicode UTF-8的区别
如果觉得此地排版不好,欢迎访问我的博客 浅显总结ASCII Unicode UTF-8的区别 制作表单时,为了追求更好的用户交互体验,常常会有提示性的内容,比如提醒用户字符的限制.由于英文,中文字符的 ...
- 字符编码 ASCII unicode UTF-8
字符串也是一种数据类型,但是,字符串比较特殊的是还有一个编码问题. 因为计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理.最早的计算机在设计时采用8个比特(bit)作为一个字节(b ...
- 【转】【编码】ANSI,ASCII,Unicode,UTF8之一
不同的国家和地区制定了不同的标准,由此产生了 GB2312.GBK.GB18030.Big5.Shift_JIS 等各自的编码标准.这些使用多个字节来代表一个字符的各种汉字延伸编码方式,称 ...
- ASCII,unicode, utf8 ,big5 ,gb2312,gbk,gb18030等几种常用编码区别(转载)
原文出处:http://www.blogjava.net/xcp/archive/2009/10/29/coding2.html 最近老为编码问题而烦燥,下定决心一定要将其弄明白!本文主要总结网上一些 ...
- ASCII, UNICODE, UTF-8, 字符集理解
字符编码的发展历史 一个字节:最初一个字节的标准是混乱的,出现过4位.6位.7位的一字节标准,最终由于历史原因和物理存储需求(8位是2的3次方,方便物理存储),所以采用了8位为一个字节的标准. ASC ...
- ascii unicode utf-8 url编码
ascii 编码 计算机内部,所有信息最终都是一个二进制值 上个世纪60年代,美国制定了一套字符编码ascii ascii 编码就是定义:英语字符与二进制位之间的关系 unixcs unicode编码 ...
- 理解记忆三种常见字符编码:ASCII, Unicode,UTF-8
理解什么是字符编码? 计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理.最早的计算机在设计时采用8个比特(bit)作为一个字节(byte),所以,一个字节能表示的最大的整数就是25 ...
- 【转】关于字符编码,你所需要知道的(ASCII,Unicode,Utf-8,GB2312…)
转载地址:http://www.imkevinyang.com/2010/06/%E5%85%B3%E4%BA%8E%E5%AD%97%E7%AC%A6%E7%BC%96%E7%A0%81%EF%BC ...
- 关于字符编码,你所需要知道的(ASCII,Unicode,Utf-8,GB2312…)
字符编码的问题看似很小,经常被技术人员忽视,但是很容易导致一些莫名其妙的问题.这里总结了一下字符编码的一些普及性的知识,希望对大家有所帮助. 还是得从ASCII码说起 说到字符编码,不得不说ASCII ...
- 字符编码:ASCII,Unicode,UTF-8
1.ASCII码美国制定的一套字符编码,对英语字符和二进制位之间的关系,做了统一规定.ASCII码一共规定了128个字符(包括32个不能打印出来的控制符号)的编码,占用一个字节,字节的最前面1位统一为 ...
随机推荐
- 【驱动】串口驱动分析(三)-serial driver
简介 前两节我们介绍串口驱动的框架和tty core部分.这节我们介绍和硬件紧密相关的串口驱动部分. UART驱动部分依赖于硬件平台,而TTY驱动和具体的平台无关.虽然UART部分依赖于平台,但是不管 ...
- XSS、CSRF 以及如何防范
- 机器学习-概率图模型系列-隐含马尔科夫-维特比算法解码隐藏序列-HMM模型参数估计-36
目录 待补充 参考资料 刘建平博客 pinard
- Nacos源码 (6) Grpc概述与Nacos集成
Nacos 2.x版本增加了GRPC服务接口和客户端,极大的提升了Nacos的性能,本文将简单介绍grpc-java的使用方式以及Nacos中集成GRPC的方式. grpc-java GRPC是goo ...
- .net core 3.0 获取 IServiceProvider 实例
.net core 3.0后,获取IServiceProvider需要绕点弯路 首先,新建一个类: public class MyServiceProviderFactory : IServicePr ...
- 百度网盘(百度云)SVIP超级会员共享账号每日更新(2023.12.15)
一.百度网盘SVIP超级会员共享账号 可能很多人不懂这个共享账号是什么意思,小编在这里给大家做一下解答. 我们多知道百度网盘很大的用处就是类似U盘,不同的人把文件上传到百度网盘,别人可以直接下载,避免 ...
- MongoDB的安装使用与监控
MongoDB的安装使用与监控 下载 https://www.mongodb.com/try/download/community 我这边习惯于下载 Windows 的 MSI 进行安装 Linux ...
- [转帖]三篇文章了解 TiDB 技术内幕 - 谈调度
返回全部 申砾产品技术解读2017-06-06 为什么要进行调度 先回忆一下 三篇文章了解 TiDB 技术内幕 - 说存储提到的一些信息,TiKV 集群是 TiDB 数据库的分布式 KV 存储引擎,数 ...
- 虚拟化平台IO劣化分析
虚拟化平台IO劣化分析 背景 最近同事让帮忙做几个虚拟机进行性能测试. 本来应该搭建CentOS/Winodws平台进行相关的测试工作. 但是为了环境一致性, 使用了ESXi6.7 进行虚拟化 然后这 ...
- 随性翻译: Java的运行时参数简单总结
Java的运行时参数简单总结 简单学习了下Oracle官方的资料: https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.ht ...