Unicode 字符集、编码 相关的基本介绍


作者:shede333

主页:http://my.oschina.net/shede333 && http://blog.sina.com.cn/u/1509658847
版权声明:原创文章,版权声明:自由转载-非商用-非衍生-保持署名 | [Creative Commons BY-NC-ND 3.0][]

Unicode

 
20世纪80年代末,
位于美国加州的组织允许任何愿意支付会费的公司和个人加入,其成员包含了主要的电脑软硬件厂商,例如奥多比系统苹果公司惠普IBM微软施乐等,
组成Unicode组织的商业机构
Unicode Consortium
,和国际合作的国际标准化组织 
IEEE 
因为电脑普及和信息国际化的前提下,分别各自成立了Unicode组织[2]和ISO-10646工作小组。他们不久便发现对方的存在,大家为着相同的目的而工作。
1991年,Unicode Consortium与ISO/IEC JTC1/SC2同意保持Unicode码表与ISO 10646标准保持兼容并密切协调各自标准进一步的扩展。虽然实际上两者的字集编码相同,但实质上两者确实为两个不同的标准。


the 
Unicode Consortium
 开发了 
The Unicode Standard
 (“Unicode”) ,
目前Unicode的最新版本为V8.0,Unicode的官网“
http://www.unicode.org/” 
ISO(或者叫
IEEE
)开发了
ISO/IEC 10646 标准
通用字符集(Universal Character Set,UCS)是
ISO 10646(或称ISO/IEC 10646)标准所定义的标准字符集 两种标准字符集相互兼容,这两个组织各自发布的标准有点复杂,暂时还没完全弄清楚

通用字符集
(Universal Character Set,UCS) 
又称Universal Multiple-Octet Coded Character Set,

个人理解 ,咱通俗的说Unicode,
通用字符集UCS,其实都是一回事,都指的是Unicode字符集。
Unicode编码与
ISO 10646
的UCS(
通用字符集)
概念相对应。
下面我就统一用  Unicode

ISO/IEC 10646标准 与 Unicode标准 之间的关系,如下图:


《Unicode 字符集、编码 相关的基本介绍》

上图引用自:Universal Coded Character Set

编码

编码方案,也叫实现方式,即把Unicode字符 以何种方式写到硬盘上。
一个字符的Unicode编码是确定的。但是在实际传输过程中,由于不同
系统平台
的设计不一定一致,以及出于节省空间的目的,对Unicode编码的实现方式有所不同。Unicode的实现方式称为
Unicode转换格式
(Unicode Transformation Format,简称为UTF) UTF是“
Unicode Transformation Format”的缩写,可以翻译成Unicode字符集转换格式,即怎样将Unicode定义的数字转换成程序数据。 而UTF8,UTF16,UTF32则是规定了Unicode字符按照指定的规则存储到硬盘上了,也按照同样的规则读出来,属于编码。

Unicode有两种编码映射方法: 
Unicode Transformation Format (UTF) 编码 和   
Universal Coded Character Set (UCS) 编码  编码名字中的数字也代表一定的意思,UTF编码的数字标明每个码点的bit数(个人理解:UTF-8最少8个位, UTF-16最少16位,UTF-32最少32位),UCS编码的数字代表每个码值的字节数。

UCS-2用两个字节编码,UCS-4用4个字节编码。

UTF-8 和UTF-16是目前最通用的编码,web页面,网络传输主要用的是UTF-8,UCS相关的名称现在说的很少了。 UCS-2是UTF-16的子集,现在要是说UCS-2的话,基本上指的是UTF-16; UCS-4和UTF-32基本算是一个东西。
refer: Wiki-Unicode 后面讲解UTF-16 和UTF-32是的时候,或详细讲解他们和UCS2、UCS4的区别

强调一下,Unicode只是一个抽象的标准而已,只做映射,不涉及到编码的存储策略,和密码本类似,属于字符集,指定了一个从字符到数字的映射; Unicode的出现解决了在一篇文档里面出现多种语言的问题,在Unicode出现之前,每种字符集一般都是对应一种语言的。

Unicode可以被不同的编码方式实现,例如UTF8,UTF16,
GB 18030

《Unicode 字符集、编码 相关的基本介绍》

《Unicode 字符集、编码 相关的基本介绍》

从这里可以很清楚地看到:

  1. 编码是依赖于字符集的,就像代码中的接口实现依赖于接口一样;
  2. 一个字符集可以有多个编码实现,就像一个接口可以有多个实现类一样。

上图引用自:字符集与编码(一)——charset vs encoding

注意:
一个【字节】是8位二进制,可表示为 0xFF,也可表示为 1111 1111
【字符】,每个Unicode码位都是一个字符,“a”是一个字符,“王”也是一个字符,“😄”也是一个字符

UCS-4根据最高位为0的最高字节分成2
7=128 (2^7)个group。每个group再根据次高字节 分为256 (2^8)个平面(plane)。每个平面根据第3个字节分为256行 (row),每行有256个码位(cell)。
group 0的平面0被称作BMP(
Basic Multilingual Plane
。group这个级别极少用

0x7F         0xFF      0xFF      0xFF group       plane      row         cell 

如果UCS-4的前两个字节为全零,那么将UCS-4的BMP去掉前面的两个零字节就得到了UCS-2。


目前的
Unicode字符分为
17组
0x0000 至 0x10 FFFF,共111 4112个。( int(“0x10FFFF”, 16) = 1114111 ),
有1,112,064个码位(code point)可用来映射字符(这和UCS-4是兼容的)
每组称为平面(Plane),而每平面拥有65536(2^16)个码位, 0号平面称为
BMP
(后面有详细的介绍BMP),承载了大部分常用字符,
其他16个平面(1~16)称为
辅助平面
(Supplementary Planes)


Unicode最大字符为 0x10 FFFF,那么可以理解为21位的编码方案( bin(0x10) == 0b10000,  21 = 5+4*4 ) 虽然UCS-4的编码最大可以用31位,但是二者已经达成协议,即使新加的字符也会保证分配到前17个平面(0~16)内,即0x0 ~  
0x10 FFFF这个范围,所以说二者可以说是一致的,后面我就还是统一来Unicode这个名词来说。


这可算是二者的定义区别吧,但实际上两者是兼容的。后面我们只以Unicode为标准,编码方面以UTF来讲。

每个平面有2
16=65536 个码位。
Unicode计划使用了17个平面,一共有17×65536=111 4112个码位。 在Unicode 5.0.0版本中,
已定义的码位只有238605个(并不代表有这么多字符,只是说已经分配的,包括PUA等),分布在平面0、平面1、平面2、平面14、平面15、平面16。

虽然计划使用17个平面,但目前只有如下几个平面才被用。
《Unicode 字符集、编码 相关的基本介绍》

其中平面15和平面16上只是定义了 两个各占
65534个码位的专用区(Private Use Area),分别是0xF0000-0xFFFF
D和0x100000-0x10FFF
D。所谓专用区,就是保留给大家放自定义字符的区域,可以简写为 
PUA
(后面有对PUA的详细介绍) 平面0也有一个PUA专用区:0xE000-0xF8FF,有6400个码位; PUA区域不分配Unicode字符。

平面0的0xD800-0xDFFF,共2048个码位,是一个被称作代理区(Surrogate)的特殊区域,这个区域也不分配Unicode字符。
UTF-16就利用保留下来的0xD800-0xDFFF区段的码位来对辅助平面的字符的码位(BMP以外的字符
)进行编码
在介绍UTF-16编码时会介绍

如前所述在
Unicode 5.0.0版本中,
238605-65534*2-6400-2048=99089(删除平面15、16,BMP平面上的PUA,BMP平面上的用于UTF-16的代理区)。余下的99089个已定义码位分布在平面0、平面1、平面2和平面 14上,它们对应着Unicode定义的99089个字符,其中包括71226个汉字。 平面0、平面1、平面2和平面14上分别定义了52080、 3419、43253和337个字符。 平面2的43253个字符都是汉字。平面0上定义了27973个汉字。

在表示一个Unicode的字符时,通常会用
“U+xxxx”然后紧接着一组十六进制的数字(
4~6位)来表示这一个字符。在
基本多文种平面(英文为 
Basic Multilingual Plane,简写 BMP。它又简称为“零号平面”, plane 0)里的所有字符,要
用四位十六进制数(例如U+4AE0,共支持六万多个字符); 在BMP平面以外的字符则需要使用
五位或六位十六进制数了。

Unicode 到目前为止所定义的五个平面中(不包括15、16的PUA平面),第0平面(BMP)最为重要,包含了绝大部分常用字符

例如,“汉字”对应的数字是0x6c49和0x5b57,而编码的程序数据是:
《Unicode 字符集、编码 相关的基本介绍》

refer: 
Unicode 百度百科 refer: 
Unicode官网—Unicode最新版本介绍
refer: Unicode 最新版 Character Code Charts (查找每个字符的详细解释) refer: 
Wiki—Plane (Unicode)

Basic Multilingual Plane(BMP)

BMP,
基本多文种平面
又称为“零号平面”、plane 0, 包含了大部分的常用字符,然而,大部分的字符都分配给CJK了
BMP的字符的编码为
U+hhhh
,其中每个
h
代表一个
十六进制
数字,与UCS-2编码完全相同

其中 (U+D800–U+DBFF) 和  (U+DC00–U+DFFF) 码点被UTF-16占用了,所以这部分不分配任何字符。

65,392 of the 65,536 code points in this plane have been allocated to a 
Unicode block, leaving just 144 code points in unallocated ranges 
(64 code points at 0860..089F, 64 code points at 1C80..1CBF, and 16 code points at 2FE0..2FEF).

《Unicode 字符集、编码 相关的基本介绍》

A map of the Basic Multilingual Plane. Each numbered box represents 256 code points.

refer: 
Wiki – Basic Multilingual Plane refer: 
Wiki-Unicode block (
Unicode 的code point,即Unicode的分割区域所代表的意思)

Private Use Areas

在Unicode标准中,
Private Use Area
 (
PUA
)范围内的字符不会被分配出去,
目前,PUA有3个区域:

  1. BMP平面的(U+E000U+F8FF),
  2. planes 15 (U+F0000U+FFFFD)
  3. planes 16 (U+100000U+10FFFD)

three private use areas are defined: one in the 
Basic Multilingual Plane
(U+E000
U+F8FF
), and one each in, and nearly covering, 
planes 15 and 16
 (U+F0000
U+FFFFD
,U+100000
U+10FFFD
). 在这3个区域内的字符不能被分配给Unicode字符。Unicode是故意不分配这部分区域,目的是其他的第三方机构可能会定义他们自己的字符,以此来避免与Unicode产生冲突

refer: 
Wiki—Private Use Areas

CJK

中日韩统一表意文字(CJK Unified Ideographs),目的是要把分别来自中文、日文、韩文、越文中,本质、意义相同、形状一样或稍异的表意文字(主要为汉字,但也有仿汉字如日本国字、韩国独有汉字、越南的
喃字)于ISO 10646及
Unicode标准内赋予相同编码。
CJK 是中文(Chinese)、日文(Japanese)、韩文(Korean)三国文字的缩写。顾名思义,它能够支持这三种文字。实际上,CJK 能够支持在 LaTeX 中使用包括中文、日文、韩文在内的多种亚洲双字节文字。

注:中、日、韩文字里面相同的字用的是同一个Unicode字符,这一点很有争议,因为,虽然是同一个字,但是在不同的语言里面应该是不同的字形(字体),但是Unicode这样的规定就导致了无法给这个字符加上多种字体了。

refer: 
Wiki中文版 – CJK (中日韩统一表意文字) refer: 
CJK百度百科 refer: 
字体编辑用中日韩汉字Unicode编码表(注意,这只是汉字的一部分而已)

作者:shede333

主页:http://my.oschina.net/shede333 && http://blog.sina.com.cn/u/1509658847
版权声明:原创文章,版权声明:自由转载-非商用-非衍生-保持署名 | [Creative Commons BY-NC-ND 3.0]

UTF-8

UTF-8的特点是对不同范围的字符使用不同长度的编码。变长编码 对于0x00-0x7F之间的字符,UTF-8编码与
ASCII编码完全相同。 UTF-8编码的
最大长度是6个字节。从下表可以看出,6个字节的模板有31个x,即可以容纳31位二进制数字。Unicode的最大码位0x7FFF FFFF也只有31位。 在这些编码中,性能最好,在绝大多少的网页中使用的都是UTF-8
注:bin(0x7FFFFFFF) = ‘0b1111111111111111111111111111111’ 这个31位2进制
注:中文范围 4E00-9FBF,也就是说每个中文字符的UTF-8编码都是3个字节

《Unicode 字符集、编码 相关的基本介绍》

注意:USC-4 的规定里,最大可以表示31位的字符,2^31 == 0x7FFF FFFF,UTF-8理论上可以全部表示出来, 实际中,我们使用Unicode规定的17个平面(0x0~0x10 FFFF),由此可看出,UTF-8最多用
4个字节即可表示Unicode目前已分配的字符

测试: 文档存储“哈”这1个字,保存成utf-8格式(不带BOM),然后使用HexMiner来查看其真实内容:E59388 使用python查看 “哈”字的string存储内容 ‘\xe5\x93\x88’ ,和上面是一致的  

refer: 
Wiki UTF-8

UTF-16

UTF-16是
变长编码,UTF-16是UCS最初版本的第一个修正案,是UCS-2的一个扩展,以此来表示BMP平面之外的码点
以下摘自“Wiki UTF-16”的翻译:
————-
“Universal Character Set” (UCS)
在上世界80年代晚期,就开始了UCS计划,最开始时打算用2个字节(2^16==65536)来代表一个字符,
最开始有两个组织干这个事情,
 
IEEE 
and the 
Unicode Consortium

Unicode Consortium组织代表的是计算机设备制造商,二者互相同步各自的字符分配策略,以便能够互相兼容;
早期,
这种的两字节编码被称为“Unicode”,但是现在被称作“UCS-2”;
在指定Unicode的中早期,就发现65536字符(即两字节编码)是不够用的,IEEE就引进了UCS-4标准(即每个字符需要4个字节来表示),但是这个决定却被
Unicode Consortium组织反对,反对的原因有两个:

  1. 一个字符用4个字节表示会浪费硬盘和内存空间;
  2. 许多计算机制造商已经在UCS-2技术上投入了大量资金;

在发布Unicode 2.0版本的时候,UTF-16就被IEEE开发出来解决这两个组织的僵局。
———

UTF-16编码以16位无符号整数为单位。我们把Unicode编码记作U。编码规则如下:
现在定义一个新单位,定义2个字节(即16位)为一个Word,那么以上U<0x10000编码的的字符就是是一个Word; 如果U<0x10000,U的UTF-16编码就是U对应的16位无符号整数(为书写简便,下文将16位无符号整数记作WORD)。 如果U≥0x10000,我们先计算U’=U – 0x10000,然后将U’写成二进制(5个字节)形式:yyyy yyyy yyxx xxxx xxxx, 那么U的UTF-16编码(二进制)就是:110110yyyyyyyyyy 110111xxxxxxxxxx。

按照上述规则,Unicode编码0x10000-0x10FFFF的UTF-16编码有两个WORD,第一个WORD的高6位是110110,第二个WORD的高6位是110111。可见, 第一个高位WORD的取值范围(二进制)是
1101 1000 0000 0000到
1101 1011 1111 1111,即
0xD800-0xDBFF。 第二个低位WORD的取值范围(二进制)是
1101 1100 0000 0000到
1101 1111 1111 1111,即
0xDC00-0xDFFF。 为了将一个WORD的UTF-16编码与两个WORD的UTF-16编码区分开来,Unicode编码的设计者将
0xD800-0xDFFF保留下来,并称为代理区(Surrogate):

由上所知:

当在一个 UTF-16 编码的字符串里发现在
0xD800-0xDFFF这个范围内的序列时,就能立刻知道这是某个代理对的一部分 即在UTF-16中,对于U<0x10000的字符,UTF-16编码和Unicode编码都用两个字节表示,而且编码内容也相同; 对于U≥0x10000这部分字符,需要使用使用两个Word来表示(即4个字节),而且每个Word的最高位被定死了,第一个Word的最高6位为110110, 第2个Word的最高6位为110111, 可以想象,之所以把两个Word的最高位定死,就是为了把这种两个Word表示一个字符的 和 一个Word表示一个字符的区分开,那么,如果第一个Word的110110******可以表示一个正常的Unicode字符的话,那么我们就分不清110110******到底是表示一个字符呢,还是表示两个Word的高位。 注:在提出UTF-16之前(即UCS-2)上
0xD800-0xDFFF是有分配Unicode字符的,在推出UTF-16之后才把这部分的字符分配去掉的。

Example 1:

用十进制做一个例子,一开始要求要表示50个字符: 那么我们规定用两位十进制来表示一个字符,这样我们就能表示100个字符了(0~99),而我们只用了前50个字符,后50个字符都还没分配; 然后来有需求变更了,需要表示的字符增加到了 120 个了,同时要求兼容之前的50个字符标准: 那么我们规定,对于  0~79的字符 只能使用2位十进制,
十位只能使用0~7,
个位随意,这样就能用两位十进制表示80个字符; 对于大于80的字符,我们使用2个十进制数(即4位)来表示一个字符,这两个十进制分为高位、低位,高位数的十位只能为8, 低位数的十位用9

对于小于80的数字 ,例如要表示数字12,那么就用 12 来表示 对于大于等于80的数字需要使用两个十进制数字表示,例如要表示数字80,那么就用 80 80,其余的表示方法如下

原始字符       编码后的表示方式 80                       80 90 81                       80 91 82                       80 92 83                       80 93 …….. 90                       81 90 91                       81 91 92                       81 92 …….. 100                     82 90 101                     82 91 102                     82 92

这样,当出现以下编码,我们就知道表示什么意思了(忽略空格) 编码后(存在硬盘上的内容)  34 
82 95 56 
80 94   要表达的原始字符               34 
105 56 
84

Example 2:

《Unicode 字符集、编码 相关的基本介绍》

个人理解:
下面只针对于UTF16大于0x10000部分, 由于目前Unicode的范围是0x0 ~ 0x10FFFF,BMP平面之外的字符范围是 0x1 0000 ~ 0x10 FFFF; Unicode表示 0x10 FFFF个字符,那么需要21个bit位(bin(0x10) = 0b10000 即5位二进制,21 = 5+ 4*4),UTF-16需要把这些字符都表示出来 但是UTF16需要减去0x1 0000,那么就相当于 UTF16 用 0x0 ~ 0xF FFFF 去表示Unicode的 0x1 0000 ~ 0x10 FFFF,(0x10 FFFF – 0xF FFFF  == 0x1 0000 ) 这么看的话UTF16 只需要 表示0xF FFFF个字符即可,即20个bit位,那么我们来算一下,UTF-16的这种2个Word能不能表示出来这20个bit位。 UTF16一个Word是两个字节(16个bit位),然后减去6个高位bit,那么一个Word的有效bit位是10,两个Word的有效bit位正好也是20, 这样看的话,UTF-16这种方法,用两个Word正好可以表示Unicode 大于0x1 0000的部分(BMP平面以外的部分)。

UTF-16,大部分字符使用
两个字节就可以表示,对于unicode的U≥0x10000,就要使用
四个字节了(即两个Word)。

创建一个文本,里面内容为“你好”,保存为 UTF-16 BE  ,大端 那么文件的存储内容为
《Unicode 字符集、编码 相关的基本介绍》

大端的BOM为 FEFF,你(\u4f60) 好(\u597d),这就明白上面的存储的内容了吧

refer: 
Unicode 百度百科 refer: 
Wiki UTF-16

UTF-32

最初,
 
ISO 10646
 定义了31位编码标准,叫做UCS-4,UCS里的每一个字符都是用32位码点表示(0 ~ 0x7FFF FFFF).
UCS-4是一个更大的尚未填充完全的31位字符集,加上恒为0的首位,共需占据32位,即4字节。理论上最多能表示2
31
个字符,完全可以涵盖一切语言所用的符号。 由于目前仅仅有17个平面在使用(0~0x10),所有的码点都在
 0 and 0x10FFFF范围内,这就是UTF-32的编码范围,所以说,UTF-32是UCS-4的子集; 根据
 
JTC1/SC2/WG2规定,未来分配的新字符都被限制在 BMP平面(0 plane) 和前14 个平面内(15、16平面只能用于PUA),这样UTF-32就能表示所有的Unicode字符,所以UCS-4和UTF32是一致的。

UTF-32编码以32位无符号整数为单位。Unicode的UTF-32编码就是其对应的32位无符号整数。 定长编码,4字节表示一个字符,而且UTF-32编码的值 和 Unicode标准的值一一对应,相对于其他变长编码,UTF-32的优点如下: 从编码值找到要表示的Unicode值是很快的,花费的时间是固定的,应为二者的值是一样的,而变长编码需要花费一些时间去做转换; UTF-32最大的缺点:太浪费空间了,因为在文本里面,大部分都是BMP平面的字符

字节序: 字节序有两种,分别是“大端”(Big Endian, BE)和“小端”(Little Endian, LE)。 在UTF-16中,字节序是以
2个字节为单位的,在每个单位内分 
字节序(见下面高亮部分); 在UTF-32中,字节序是以
4个字节为单位的,在每个单位内分 
字节序

根据字节序的不同,UTF-16可被实现为UTF-16LE或UTF-16BE,UTF-32可被实现为UTF-32LE或UTF-32BE,如下图:

Unicode编码 UTF-16LE  UTF-16BE  UTF32-LE  UTF32-BE
0x6C49 49 6C 6C 49 49 6C 00 00 00 00 6C 49
0x020C30 43 D8 30 DC D8 43 DC 30 30 0C 02 00 00 02 0C 30

refer: 
Unicode 百度百科 refer: 
Wiki UTF-32

BOM(Byte Order Mark)

字节序 Unicode标准建议用
BOM(Byte Order Mark)来区分字节序,即在传输字节流前,先传输被作为BOM的字符“零宽无中断空格”。
如果
最低有效位

最高有效位
的前面,则称
小端序
;反之则称
大端序 比如数字 12,最低有效位是个位数 1,一般来说,内存地址都是从左到右递增,那么 小端的存储就是  2 1 大端的的存储是  1 2 以上同理可以应用到16进制表示法上。

在内存里存储字符串时,大多数实现方式自然都采用自己运行平台的 CPU 的字节序(endianness);而在硬盘里存储或者通过网络传输字符串时,UTF-16 允许在字符串的开头插入一个「字节序标记」(Byte Order Mask,BOM) UTF-16 需要指明字节顺序,这也是为什么 UTF-16 在文件格式和网络传输方面不受欢迎的一个原因,不过微软和苹果都在自己的操作系统内部使用它。

下表是各种UTF编码的BOM:

UTF编码 Byte Order Mark (BOM)
UTF-8 without BOM
UTF-8 with BOM EF BB BF
UTF-16LE FF FE
UTF-16BE FE FF
UTF-32LE FF FE 00 00
UTF-32BE 00 00 FE FF

由上所知: UTF-32编码使用
4个字节 一般来说,只有UTF-16 和UTF-32需要使用字节序,UTF-8很少需要,因为UTF-8就一种字节序, 使用python里的文件读取操作读取正常的UTF-8文件(没有BOM),
open(“文件路径”, “rb”).read() 即可获得内容 如果读取带有BOM的UTF-8文件,open(“文件路径”, “rb”).read() 获取的字符中,前几位会乱码,即BOM,如果这是一个json文件,那么一些json库就无法正确读取这些文件 要读取带有BOM的UTF-8文件,获取的内容又不带BOM,那么就要用 
open(“文件路径”, “rb”).read().decode(“utf-8-sig”)

refer: 
Wiki中文 – BOM 字节序 refer: 
Wiki – BOM (Byte order mark)

Unicode In iOS

@”\U0001f604” 等同于字符 😄 ,当此字符串的长度 是 
2 @”\u54c8” 等同于 @“哈” , 字符长度为1 就是说把BMP平面以外的字符长度当成2了 在苹果官方文档中
 The Unicode Basis of CFString Objects中, “
CFString 代表了一个 Unicode 字符组成的数组和一个字符总数的计数。……Unicode 标准定义了一个通用、统一的编码方案,其中每个字符 16 位。” 这应该就是计算BMP平面以外字符的长度错误的原因了吧,每个字符16位是UCS-2那个时代的事情了

如果你很在意长度的话,要解决计算长度计算错误,可以转为UTF32后再去计算啊,看样子UTF32也不是一无是处啊
NSString *s = @”\U0001F30D”; // earth globe emoji  
NSLog(@”The length of %@ is %lu”, s, [s length]);  // 长度为 2
NSUInteger realLength = [s lengthOfBytesUsingEncoding:NSUTF32StringEncoding] / 4; // realLength 为1

另外对 组合字符序列,变体序列的长度计算也是有问题的,这个就不能用UTF-32解决 例如 加重音符号的e是“é”,é用Unicode表示的话需要两个字符组合起来,即小写e加重音符号
NSString *s = @”e\u0301″;
NSLog(@”%@,%d”, s, s.length);  // 输出  é,2 但实际上é对我们来说算一个字符的,长度应该为2,这种问题的处理方法也是有的,但是这里就不展开说明了,代码如下:
NSString *n = [s precomposedStringWithCanonicalMapping]; 
NSLog(@”The length of %@ is %lu”, n, [n length]); 
// => The length of é is 1

é既可以使用上面说的组合方法表示,同时é其实也是单个Unicode字符”\u00e9″
NSString *s1 = @”\u00e9″;
NSString *s2 = @”e\u0301″;
NSLog(@”%@, %@, %d”, s1, s2, [s1 isEqualToString:s2]);  //输出结果: é, é, 0 s1,s2明明是一样的字符,但是isEqualToString方法去返回NO,这是因为NSString的“isEqualToString”方法只是一个个字节的比较,所以会得出不相等的结论 解决办法如下:
// Normalizing to form C
NSString *sNorm = [s precomposedStringWithCanonicalMapping];
NSString *tNorm = [t precomposedStringWithCanonicalMapping];
BOOL isEqualNorm = [sNorm isEqualToString:tNorm];
NSLog(@”%@ is %@ to %@”, sNorm, isEqualNorm ? @”==” : @”!=”, tNorm);  
// 输出 é == é

Apple在PUA区域里面定义了一下字符(
文档点这里),虽然大多数已经不再使用了,但是苹果的 logo 是个著名的例外:,它的码点是 U+F8FF。(你可能看到的是另一个不同的字符,这取决于你阅读本文的平台,理论上只有在Apple平台上才能看到这个字符)。

NSString 代表的是用 UTF-16 编码的文本,长度、索引和范围都基于 UTF-16 的码元。 个人理解:NSString之所以在BMP以外的字符长度计算错误,是为了保证快速的(时间复杂度 O(1) 级别)与 UTF-16 码元转换。

NSString 里面想要插入Unicode字符,可用如下方式: BMP平面内(Unicode的码值 <  0x10000 ):@“\u266A”(♪)的方式输入,
最头上的u要小写,后面的其他字符不区分大小写 也可以使用这种方式 @“\U0001F340”(🍀 )的方式输入(
最头上的U要大写),这种方式主要为BMP平面以外的部分用,当然,BMP平面内的也可以用 另外,C99 不允许标准 C 字符集里的字符用通用字符名(universal character name)来指定,因此不能这样写  
NSString *s = @”\u0041″ 我测试了一下,ASCII码的都不能用,后128位部分不能用。

这部分还没看完,待续~~~

refer: 
NSString & Unicode – 没名儿呢 – 知乎专栏 refer: 
苹果官方文档 The Unicode Basis of CFString Objects

ANSI编码

(American National Standards Institute),中文:美国国家标准学会。

不同的国家和地区制定了不同的标准,由此产生了 GB2312、GBK、Big5、Shift_JIS 等各自的编码标准。这些使用 1 至 4 个字节来代表一个字符的各种汉字延伸编码方式,称为 ANSI 编码。在简体中文Windows操作系统中,ANSI 编码代表 GBK 编码;在日文Windows操作系统中,ANSI 编码代表 Shift_JIS 编码。 不同 ANSI 编码之间互不兼容,当信息在国际间交流时,无法将属于两种语言的文字,存储在同一段 ANSI 编码的文本中。 当然对于ANSI编码而言,0x00~0x7F之间的字符,依旧是1个字节代表1个字符。这一点是ANSI编码与Unicode编码之间最大也最明显的区别。

refer: 
百度百科—ansi编码

ASCII

“Ascii”编码(American Standard Code for Information Interchange,美国信息互换标准代码)。 在windows中,
对于英文系统即
ASCII
编码,中文系统则为
GB2312

Big5
编码

Unicode 额外介绍

再说一下,这些Unicode字符不是单独存在的,他们可以互相组合的, 例如,有重音符号的字母 é 可以直接表示成 U+00E9(「有尖音符号的小写拉丁字母 e」),或者也可以表示成由 U+0065(「小写拉丁字母 e」)再加 U+0301(「尖音符号」)组成的分解形式。这两个形式都是组合字符序列的变体; 再就是,许多看上去一样的字符都在不同的码点编码了多次,以此来代表不同的含义。 对以上内容感兴趣的话,可以搜索 
相容等价(compatibility equivalence) 、 
标准等价(canonically equivalent),组合字符序列,变体序列

以上参考自:NSString & Unicode

注意:

1.百度百科上的介绍有些错误,更新也不及时,
强烈建议到Wiki上看资料 2.用Python查看UTF-8编码后的内容是没问题的,估计是因为UTF-8的每个字节都是1开头的,越过了ASCII码, 而用查看UTF-16编码后的内容,有很多字符被解释成ASCII码后,就看不到原始内容了 3.使用
Python查看Unicode字符的一些方法, 想要查看某汉字的码点,比如查看 “好”字的码点: 

好”.decode(“utf-8”)  //注意:后面的“utf-8”是平台的默认编码,如果你的平台默认编码不是utf-8的话,改你你当前平台的默认编码即可。 想要查看某码点代表的字符,例如查看“U+597d”这个码点代表的字符:
print u”\u597d” 或者 
print u”\U0000597d”; 4位16进制数字(
\u597d)的表示方法:小写的u ,加“\” ,加 4位Unicode的16进制的码点; 8位16进制数字(
\U0000597d)的表示方法:大写的U ,加“\” ,加 8位Unicode的16进制的码点; 对于小于0x1 0000的字符(BMP平面内的),4和和8位的表示方法都可以; 对于大于等于0x1 0000以外的字符,必须使用8位16进制数字表示方法。 4.上面引用的一些图片,想要查看出处,点击图片即可。

工具:

  1. Hex Friend
  2. File Info Professional

参考:

注意,下面的中文文档并不是英文版的翻译,二者不太相关,但英文版的介绍更丰富,建议看看英文版的

Wiki中文版 – Unicode
Wiki中文版 – UCS (通用字符集)
Wiki中文版 – UTF-8
Wiki中文版 – CJK (中日韩统一表意文字)
Wiki中文版 – BOM 字节序

Wiki – Unicode  
Wiki – UCS (Universal Coded Character Set)
Wiki – UTF-8
Wiki – UTF-16
Wiki – UTF-32
Wiki – BMP平面 (Basic Multilingual Plane) 
Wiki – Comparison of Unicode encodings(Unicode各自编码之间的比较)
Wiki – Plane (Unicode),Unicode平面
Wiki – Private Use Areas (PUA)
Wiki – Unicode block (Unicode 的code point,即Unicode每个区域所代表的语言)
Wiki – CJK
Wiki – BOM (Byte order mark)

苹果官方文档 The Unicode Basis of CFString Objects

Unicode官网 – www.unicode.org/
Unicode官网 – Unicode最新版本介绍
Unicode官网 –  最新版 Character Code Charts (查找每个字符的详细解释)

字体编辑用中日韩汉字Unicode编码表(注意,这只是汉字的一部分而已)

另外:

对字符编码感兴趣的话,推荐看下以下的文章:

字符编码介绍:字符集与编码(1~9章)

    原文作者:移动开发
    原文地址: https://my.oschina.net/shede333/blog/668438
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞