点击上方 程序员成长指北,关注公众号
回复1,加入高级Node交流群
本文字数:5425字
预计阅读时间:14 分钟
字符编码的奥秘
Unicode
、UTF-8
、GBK
字符集,以及emoji
。byte
即足以表示出来。这个就是大家所熟知的ASCII
编码。对应关系很简单,一个字符对应一个byte
。ASCII
码,不同国家推出了自己不同的编码方式,中国的gb2312
就是我们国家自己推行的编码方式,这样下去每个国家都有自己的编码方式,来回转换太麻烦了;这时候大家当然想统一字符编码,这时候出现了新的编码方式,unicode
编码方式,将编码统一,规定了每个字符对应的unicode
码。Unicode概述
Unicode
(统一码,万国码)是基于通用字符集(Universal Character Set
)的标准发展。它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足语言、跨平台进行文本转换、处理的要求。Unicode
是1988年由Apple和Xerox共同建立的一项标准。1991年,成立专门的协会来开发和推动Unicdoe
。Unicode
用数字0到0X10FFFF来映射这些数字,最多容纳1114112个字符。1114112是怎么计算出来的?将0X10FFFF分成0X10和0XFFFF两部分。我们知道0XFFFF是65535,那么 [0,65535] 区间内,总共是65536个。同理,0X10用10进制表示为16,那么 [ 0,16 ] 左右闭区间,总共是17个(我们称之为17个平面)。所以17乘以65536 = 1114112。世界上各种字符,用 Unicode 排布
UCS-2
用2个字节编码和UCS-4
用4个字节编码。那么0X10FFFF即UCS-4
了。UCS-4
根据最高位为0的最高字节分成2^7=128个group。每个group再根据次高字节分为256个平面(plane)。每个平面根据第3个字节分为256行 (row),每行第4字节有256个码位(cell)。每个平面有2^16=65536个码位。如图1:PUA
,最重要的是下面的0-1-2三个平面,特别是第0个平面,也就是下图中色彩斑斓的平面;(图2)
Unicode
计划使用了17个平面,一共有17*65536=1114112个码位。在Unicode 5.0.0
版本中,已定义的码位只有238605个,分布在平面0、平面1、平面2、平面14、平面15、平面16。其他平面尚未使用,如下图所示:(图3)
(图4)
Surrogate
)的特殊区域。代理区的目的是:用两个UTF-16
字符表示BMP
以外的字符。如前所述在Unicode 5.0.0
版本中,238605-65534*2-6400-2048=99089。余下的99089个已定义码位分布在平面0、平面1、平面2和平面14上,它们对应着Unicode
目前定义的99089个字符。平面0、平面1、平面2和平面14上分别定义了52080、3419、43253和337个字符。中国汉字的Unicode
位置,71226个汉字分别分布在两个平面上,第0个平面上有27973个常用汉字,第2个平面43253个汉字,你会发现第2个平面上清一色都是汉字。UTF-8 编码
UTF-8
编码,utf-8
是Unicode
的一种存储、传输方式,是一种可变长编码方式;是Unicode
实现方式之。UTF
是“Unicode/UCS Transformation Format
”的首字母缩写。Unicode
是字符集也是编码集,UTF-8
是编码集;UTF-8
以字节为单位对Unicode
进行编码。它是可变长的编码方式的从表1中也能开出来。从Unicode
到UTF-8
的编码方式如下:(图5)
Unicode
编码是0x6C49。0x6C49在0x0800-0xFFFF之间,使用用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将0x6C49写成二进制是:0110 1100 0100 1001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。因为,常用汉字第一个的编码从0X3e00,所以常用汉字的UTF8
都是三个字节。如果在平面2中的汉字必须4个字节了。如果你用C语言做过协议栈,就会清楚记得,给汉字开辟内存时候要使用4个字节。NSPredicate* predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",@"[\u4e00-\u9fa5]"];
**_判断是否为中文的正则表达式_**
if([predicate evaluateWithObject:name]){
是中文
}else{
不是中文
}
(图6)
Unicode
编码是0xD84C-DEAB(这是代理区)。不在[e4E00-9FA5]区间。但,此正则表达式对于鉴别常用汉字已经是足够使用了。代理区
emoji
——😊微笑,然后通过UTF-8
转化工具,看看它的编码是这样:(图7)
Unicode
编码是 D83D-DE03。utf-8
的编码是F09F-9883。常见字符Unicode
很少四个字节的,这个不寻常的。通常utf-8
是1到3个字节的,也就是说在Unicode
编码空间的第0个平面上。可以简单推测:既然emoji
的utf-8
是4个字节,说明在1,2…16个平面中的某一个
我们再看看“微笑”的emoji
符号的Unicode
:D83D-DE03,已经超过了表格中最大的0X10FFFF了,怎么回事???下面我们根据utf-8
的值:F09F-9883.来反推Unicode
对应的数值吧,看看究竟是为什么:(图8)
Unicode
:D83D-DE03的值相差很大,所以,中间肯定经过了一些转换步骤,这个转换就是utf-16
的代理!!!UTF-16
UTF
是“Unicode/UCS Transformation Format
”的首字母缩写,即把Unicode
字符转换为某种格式之意。Unicode
两个字节,在转化uft-8
的时候,根据协议,两个两个字节,对应一个uft-8
这样完成转化或者称为映射!lead surrogates
),0xDC00——0xDFFF是后尾代理(trail surrogates
)。utf-16
的字符。就那emoji
的微笑来说,前导是代理:D83D;后尾代理是:DE03。根据下图可以得出utf-16
的值是:0x1-F603。这就照应上了。utf-16
编码。(图9)
if(Unicode第一个字节 >=0xD8 && Unicode <=0xDB){
这是代理区域,表示第1——16平面的字符。每四个字节表示一个单元
}
else{
这是正常映射区域,表示第0个平面。每两个字节表示一个单元。
}
Emoji
并不都是四个字节。举个例子有些复杂表情会更长:比如👩👩👦👦,使用NSString
标示时候:str.length
是11,data.length
是25:他是由四个emoji
组成的。只不过在编码的开始加入了特殊的控制字符。👩👩👦👦=👩+👩+👦+👦NSString* str = @“👩👩👦👦”;
NSData* data = [str dataUsingEncoding:NSUTF8StringEncoding];
NSLog(@“emoji str-len:%d data-len:%d”,str.length,data.length);
emoji str-len:11 data-len:25
。iOS 上实现显示汉字的 Unicode 和 UTF8 代码
NSString
的方法dataUsingEncoding
,参数可以选择NSUnicodeStringEncoding
和NSUTF8StringEncoding
然后得到NSData
;GBK
需要自己生成一个NSStringEncoding
,代替NSUTF8StringEncoding
。NSStringEncoding gbkEncoding =CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
NSData
包含了首地址和长度。比如“联通”经过Unicode
后是4个字节,UTF-8
之后是6个字节,都可以从NSData
中得到。然后,用char*
指向NSSdata
后,逐个将char
打印出来就行。如下图所示:(图10)
ANSI
(在中国是GB2312
),而
“联”ANSI
编码是 0xC1AA 二进制排列是 1100 0001 1010 1010;“通”ANSI
编码是 0xCDA8 二进制排列是 1100 1101 1010 1000;开头是110开头,再次打开记事本,系统会使用utf8
打开。就会异常。现在的操作系统会存储一个BOM
,来表明是那种编码避免了乱码的问题。GBK 简单介绍
GBK
——专门为解决汉字的编码而生成的解决方案。那么,一个汉字究竟被存储为什么,就需要:先查unicode
码表,然后根据在码表的位置进行计算。例如:“电”字,在码表中是3575,而在GB2312
的码表中为B5E7。GBK
的中文编码是双字节来表示的,英文编码是用ASCII
码表示的,即用单字节表示。但GBK
编码表中也有英文字符的双字节表示形式,所以英文字母可以有2种GBK
表示方式。为区分中文,将其最高位都定成1。英文单字节最高位都为0。当用GBK
解码时,若高字节最高位为0,则用ASCII
码表解码;若高字节最高位为1,则用GBK
码表解码。ASCII
保持一致,此范围内严格上说有96个文字和32个控制符号。之后的双字节中,前一字节是双字节的第一位。总体上说第一字节的范围是81–FE(也就是不含80和FF),第二字节的一部分领域在40–7E,其他领域在80–FE。具体来说,定义的是下列字节:(图11)
GBK
的编码,红色是用户定义区域。没有颜色的区域是不正确的代码组合:(图12)
GBK
编码是GB2312
编码的超集,向下完全兼容GB2312
。GB18030
编码向下兼容GBK
和GB2312
,GBK
、GB2312
等与UTF8
之间都必须通过Unicode
编码才能相互转换。GBK
,GB2312
以及Unicode
都既是字符集,也是编码方式,而UTF-8
只是编码方式,并不是字符集。
我组建了一个氛围特别好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你对Node.js学习感兴趣的话(后续有计划也可以),我们可以一起进行Node.js相关的交流、学习、共建。下方加 考拉 好友回复「Node」即可。
“分享、点赞、在看” 支持一波👍