代码点与代码单元和Unicode相关的UTF

java字符串由char序列组成,char数据类型是一个采用UTF-16编码表示Unicode代码点的代码单元,大多数的常用Unicode字符使用一个代码单元就可以表示,而辅助字符需要一对代码单元来表示,length方法返回的是采用UTF-16编码表示的给定字符串所需要的代码单元的数量,要想得到真实的长度即代码点的数量可以调用xxx.codePointCount(0,xxx.length())方法。
为什么那么关注代码点和代码单元,因为在处理字符串时使用length和charAt方法时返回的是编码表示下的代码单元数量而不是认为的字符个数,在一个字符串内如果有使用多个代码单元表示的字符,在使用以上的方法时会出现检索错误。

那什么是代码点什么是代码单元?
代码点(code point):unicode是属于编码字符集(CSS)的范围,将需要表示的字符表中的每个字符映射成一个数字,这个数字被称为码点,代码点是字符集被编码后出现的概念,为了计算机使用这些所以出现了编号,这些编码了的字符集叫做编码字符集这个编号i就是代码点,指与一个编码表中的某个字符对应的代码值,在Unicode标准中代码点采用十六进制书写并加上前缀U+,Unicode代码点分为17个级别,第一个代码级别称为基本的多语言组别,代码点从U+0000到U+FFFF,其余的16个附加级别,代码点从U+10000到U+10FFFF包括了一些辅助字符
代码单元(code unit):是指一个以编码的文本中具有最短的比特组合的单元,在基本的多语言级别中每个字符用16位表示,通常被称为代码单元。

总结一下代码点是为字符分配的编号,一个字符占一个代码点,而代码单元则是针对编码方法而言的,因为一个字符可以编译为一个字节,两个字节或者三四个字节。所以一个字符对应一个代码点但是可以有多个代码单元,当你想以一种方式指定自己使用什么字符的时候使用代码点,即告诉程序你使用的是第几个字符,而在不同的情况使用不同的代码单元,因为需要区分不同的情况。

Unicode包含两个步骤首先是定义一个规范,给所有的字符指定一个唯一对应的数字,之后才是把字符对应的数字保存在计算机中,所以在保存到计算机内时就会有不同的保存方式就会有多种UTF形式,字节编码方案是从一个或者多个编码字符集到一个或多个固定宽度代码单元序列的映射,最常用的代码单元是字节,UTF-32、UTF-16、UTF-8是Unicode标准的编码字符集的字符编码方案。

UTF-8:使用一至四个字节的序列对编码Unicode代码点进行编码。U+0000至U+007F使用一个字节编码,U+0080至U+07FF使用两个字节,U+0800至U+FFFF使用三个字节,而U+10000至U+10FFFF使用四个字节。UTF-8设计原理为:字节值0x00至0x7F始终表示代码点U+0000至U+007F(Basic Latin字符子集,它对应ASCII字符集)。这些字节值永远不会表示其他代码点,这一特性使UTF-8可以很方便地在软件中将特殊的含义赋予某些ASCII字符。最大特点是一种可变长的编码方式,根据不同的符号而变化字节长度,对应于英语字母UTF-8编码和Ascii编码是一样的,但是对于n字节的符号第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10,剩下的没有提及的二进制位,全部为这个符号的unicode编码,在计算机内存中统一使用unicode编码,当需要保存到硬盘或者传输的时候就转换到UTF-8,一般在网页中也是又服务器里的unicode编码转换为UTF-8后到浏览器上。 UTF-8有点类似于Haffman编码,它将Unicode编码为00000000-0000007F的字符,用单个字节来表示; 00000080-000007FF的字符用两个字节表示 ;00000800-0000FFFF的字符用3字节表示。因为目前为止Unicode-16规范没有指定FFFF以上的字符,所以UTF-8最多是使用3个字节来表示一个字符。但理论上来说,UTF-8最多需要用6字节表示一个字符。 


UTF-16:使用一个或两个未分配的16位代码单元为单位的序列对Unicode代码点进行编码。值U+0000至U+FFFF编码为一个相同值的16位单元。增补字符编码为两个代码单元,第一个单元来自于高代理范围(U+D800至U+DBFF),第二个单元来自于低代理范围(U+DC00至U+DFFF)。这在概念上可能看起来类似于多字节编码,但是其中有一个重要区别:值U+D800至U+DFFF保留用于UTF-16;没有这些值分配字符作为代码点。这意味着,对于一个字符串中的每个单独的代码单元,软件可以识别是否该代码单元表示某个单单元字符,或者是否该代码单元是某个双单元字符的第一个或第二单元。这相当于某些传统的多字节字符编码来说是一个显著的改进,在传统的多字节字符编码中,字节值0x41既可能表示字母“A”,也可能是一个双字节字符的第二个字节。绝大多数两个字节就够了但是要看字符的Unicode要处于什么范围,有可能是两个有可能是四个

UTF-32:即将每一个Unicode代码点表示为相同值的32位整数。很明显,它是内部处理最方便的表达方式,但是,如果作为一般字符串表达方式,则要消耗更多的内存。

这时就应该了解一下字符集标准,那么首先需要区分不同的字符视图。
1.字符集(字符的抽象列表):字符集是各种文字中所包含的字符(还有其他符号)的一个抽象列表

2.作为带标量值的“代码点”的字符:字符集的每个字符都分配到了一个代码点,每个代码点都有一个特定的值称为标量值,代码点存在于代码空间中,代码空间中由许多标量值组成,这些值被划分为两个平面中:
(1)基本多语种平面:64k大小,就是上面提到的第一级别
(2)辅助多语种平面:16个64k大小,就是上面提到的附加级别

3.作为编码数据的字符:每个编码形式将字符从字符集转换为编码数据,在Unicode中通过向标量值应用某个算法来派生编码数据,这三个形式就是常说的utf格式,而代码单元就是各个形式中的单个单元,代码单元的大小等效于特定编码的位数测量单位

首先8位的字节可以组合出256种不同的状态,编号的0到32号的各种状态分别规定了特殊的用途,这种字节状态称为控制码,又继续编写到了127,这个是ASCII码,但是世界各地都开始使用计算机为了保存这些新字母就开始使用128到255的空位,这些字符集被称为扩展字符集(char类型分为有符号和无符号,默认均为有符号,在内存中是没有什么区别的都是一个字节,有符号是为了和int byte进行转换,但是对于进行字母负数就没有意义了)
这时对于中文就显得不足所以进行单独的修改,一个小于127的字符的意义与原来相同但是两个大于127的字符连在一起,就表示一个汉字前面的称为高字节(0*A1-0*F7)后面的称为低字节(0*A1-0*FE),重新编了两个字节的编码也就是全角原来127以下的称为半角,这个方案叫做GB2312是对ASCII的中文扩展,只后的GBK将不局限于低码大于127,之后的扩充为GB18030,称为双字节字符集
unicode中半角的英文字符还是全角的中文都是一个子符了,同是都是统一的两字节,UTF表示一次传输几个位,UTF-8是一种变长的编码方式使用1-4个字节来标记一个字符,unicode到UTF-8不是直接对应的而是依靠转换
unicode是一个标准定义了一个字符集,为每一个字符分配一个码位然后将码位转换为字节序列的规则,UTF是Unicode的实现方式之一
为什么unicode规定两个字节而UTF用了三个字节,因为为了解决计算机去接取一个符号对应的二进制的截取位置,如果一个字符之占一个字节,这个八位字节的第一位就为0,如果为两个字节规定第一个字节的前两位为1第一个字节的第三位为0第二个字节的第三位前两位为10,如果是三个字节第一个字节前两位为111第四位为0剩余的两个字节的前两位是10

java不是采用通常的Ascii字符集,而是采用Unicode这样的标准字符集,Ascii字符集和Unicode的区别:
开始ASCII是128个字符后来扩展到256个成为扩展ASCII,但是之后各国为了表示自己的文字利用连续的2个扩展ASCII码的区域来表示一个字或类似的方法,这些方法统称为MBCS字符集(多字节字符集),这个方法是有缺陷的,因为各个国家地区在定义字符集的时候有交集,使用不同的字符集的软件就不能在不同的环境下运行,在多语言混合的文本中会显示乱码,所以制定了Unicode字符集,对所有的文字符号进行统一编码,任何地区的软件可以在不用修改的情况下在另一个地区运行,使其支持多语言

ISO8859-1:西欧字符集
BIG5:台湾大五码,表示繁体汉字
GB2312:大陆简体
GBK:2312的扩展可以表示繁体中文
GB18030:可以表示中华民族的民族字符,比如维吾尔文等
UTF-8:最大特点是一种可变长的编码方式,使用1~4个字节表示一个符号,根据不同的符号而变化字节长度,对应于英语字母UTF-8编码和Ascii编码是一样的,但是对于n字节的符号第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10,剩下的没有提及的二进制位,全部为这个符号的unicode编码,在计算机内存中统一使用unicode编码,当需要保存到硬盘或者传输的时候就转换到UTF-8,一般在网页中也是又服务器里的unicode编码转换为UTF-8后到浏览器上。
这时候,我们就知道,要真正解决中文问题,不能从扩展ASCII的角度入手,也不能仅靠中国一家来解决。而必须有一个全新的编码系统,这个系统要可以将中文、英文、法文、德文……等等所有的文字统一起来考虑,为每个文字都分配一个单独的编码,这样才不会有上面那种现象出现。 
于是,Unicode诞生了,Unicode有两套标准,一套叫UCS-2(Unicode-16),用2个字节为字符编码,另一套叫UCS-4(Unicode-32),用4个字节为字符编码。 以目前常用的UCS-2为例,它可以表示的字符数为2^16=65535,基本上可以容纳所有的欧美字符和绝大部分的亚洲字符 。 
在Unicode里,所有的字符被一视同仁。汉字不再使用“两个扩展ASCII”,而是使用“1个Unicode”,注意,现在的汉字是“一个字符”了,于是,拆字、统计字数这些问题也就自然而然的解决了 。  但是,这个世界不是理想的,不可能在一夜之间所有的系统都使用Unicode来处理字符,所以Unicode在诞生之日,就必须考虑一个严峻的问题:和ASCII字符集之间的不兼容问题。 ASCII字符是单个字节的,而Unicode是双字节的,这就造成了一个非常大的问题:以前处理ASCII的那套机制不能被用来处理Unicode了 。 另一个更加严重的问题是,C语言使用'\0'作为字符串结尾,而Unicode里恰恰有很多字符都有一个字节为0,这样一来,C语言的字符串函数将无法正常处理Unicode,除非把世界上所有用C写的程序以及他们所用的函数库全部换掉 。 
于是,比Unicode更伟大的东东诞生了,之所以说它更伟大是因为它让Unicode不再存在于纸上,而是真实的存在于我们大家的电脑中。那就是:UTF 。 UTF= UCS Transformation Format UCS转换格式 。它将Unicode编码规则和计算机的实际编码对应起来的一个规则。现在流行的UTF有2种:UTF-8和UTF-16 。 其中UTF-16和上面提到的Unicode本身的编码规范是一致的,这里不多说了。而UTF-8不同,它定义了一种“区间规则”,这种规则可以和ASCII编码保持最大程度的兼容 。 

    下面说说中文的问题。 由于历史的原因,在Unicode之前,一共存在过3套中文编码标准。 
    GB2312-80,是中国大陆使用的国家标准,其中一共编码了6763个常用简体汉字。Big5,是台湾使用的编码标准,编码了台湾使用的繁体汉字,大概有8千多个。HKSCS,是中国香港使用的编码标准,字体也是繁体,但跟Big5有所不同。  这3套编码标准都采用了两个扩展ASCII的方法,因此,几套编码互不兼容,而且编码区间也各有不同。 因为其不兼容性,在同一个系统中同时显示GB和Big5基本上是不可能的。当时的南极星、RichWin等等软件,在自动识别中文编码、自动显示正确编码方面都做了很多努力 。 他们用了怎样的技术我就不得而知了,我知道好像南极星曾经以同屏显示繁简中文为卖点。 后来,由于各方面的原因,国际上又制定了针对中文的统一字符集GBK和GB18030,其中GBK已经在Windows、Linux等多种 操作系统中被实现。  GBK兼容GB2312,并增加了大量不常用汉字,还加入了几乎所有的Big5中的繁体汉字。但是GBK中的繁体汉字和Big5中的几乎不兼容。GB18030相当于是GBK的超集,比GBK包含的字符更多。据我所知目前还没有操作系统直接支持GB18030。 

    谈谈Unicode编码,简要解释UCS、UTF、BMP、BOM等名词 
 
    1.0、big endian和little endian 

    big endian和little endian是 CPU处理多字节数的不同方式。例如“汉”字的Unicode编码是6C49。那么写到文件里时,究竟是将6C写在前面,还是将49写在前面?如果将6C写在前面,就是big endian。还是将49写在前面,就是little endian。 
    “endian”这个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开,由此曾发生过六次叛乱,其中一个皇帝送了命,另一个丢了王位。 
    我们一般将endian翻译成“字节序”,将big endian和little endian称作“大尾”和“小尾”。 
    GB2312的两个字节的最高位都是1。但符合这个条件的码位只有128*128=16384个。所以GBK和GB18030的低字节最高位都可能不是1。不过这不影响DBCS字符流的解析:在读取DBCS字符流时,只要遇到高位为1的字节,就可以将下两个字节作为一个双字节编码,而不用管低字节的高位是什么。 

    2、Unicode、UCS和UTF 

    前面提到从ASCII、GB2312、GBK到GB18030的编码方法是向下兼容的。而Unicode只与ASCII兼容(更准确地说,是与ISO-8859-1兼容),与GB码不兼容。例如“汉”字的Unicode编码是6C49,而GB码是BABA。 
    Unicode也是一种字符编码方法,不过它是由国际组织设计,可以容纳全世界所有语言文字的编码方案。Unicode的学名是"Universal Multiple-Octet Coded Character Set",简称为UCS。UCS可以看作是"Unicode Character Set"的缩写。 
    根据维基百科全书的记载:历史上存在两个试图独立设计Unicode的组织,即国际标准化组织(ISO)和一个软件制造商的协会(unicode.org)。ISO开发了ISO 10646项目,Unicode协会开发了Unicode项目。 
    在1991年前后,双方都认识到世界不需要两个不兼容的字符集。于是它们开始合并双方的工作成果,并为创立一个单一编码表而协同工作。从Unicode2.0开始,Unicode项目采用了与ISO 10646-1相同的字库和字码。 
    目前两个项目仍都存在,并独立地公布各自的标准。Unicode协会现在的最新版本是2005年的Unicode 4.1.0。ISO的最新标准是10646-3:2003。 
    UCS规定了怎么用多个字节表示各种文字。怎样传输这些编码,是由UTF(UCS Transformation Format)规范规定的,常见的UTF规范包括UTF-8、UTF-7、UTF-16。 
    IETF的RFC2781和RFC3629以RFC的一贯风格,清晰、明快又不失严谨地描述了UTF-16和UTF-8的编码方法。我总是记不得IETF是Internet Engineering Task Force的缩写。但IETF负责维护的RFC是Internet上一切规范的基础。 

    3、UCS-2、UCS-4、BMP 

    UCS有两种格式:UCS-2和UCS-4。顾名思义,UCS-2就是用两个字节编码,UCS-4就是用4个字节(实际上只用了31位,最高位必须为0)编码。下
    UCS-2有2^16=65536个码位,UCS-4有2^31=2147483648个码位。 
    UCS-4根据最高位为0的最高字节分成2^7=128个group。每个group再根据次高字节分为256个plane。每个plane根据第3个字节分为256行 (rows),每行包含256个cells。当然同一行的cells只是最后一个字节不同,其余都相同。 
    group 0的plane 0被称作Basic Multilingual Plane, 即BMP。或者说UCS-4中,高两个字节为0的码位被称作BMP。 
    将UCS-4的BMP去掉前面的两个零字节就得到了UCS-2。在UCS-2的两个字节前加上两个零字节,就得到了UCS-4的BMP。而目前的UCS-4规范中还没有任何字符被分配在BMP之外。 

    4、UTF编码 

    UTF-8就是以8位为单元对UCS进行编码。从UCS-2到UTF-8的编码方式如下: 
    UCS-2编码(16进制) UTF-8 字节流(二进制) 
    0000 - 007F 0xxxxxxx 
    0080 - 07FF 110xxxxx 10xxxxxx 
    0800 - FFFF 1110xxxx 10xxxxxx 10xxxxxx 
    例如“汉”字的Unicode编码是6C49。6C49在0800-FFFF之间,所以肯定要用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是:0110 110001 001001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。 
    UTF-16以16位为单元对UCS进行编码。对于小于0x10000的UCS码,UTF-16编码就等于UCS码对应的16位无符号整数。对于不小于0x10000的UCS码,定义了一个算法。不过由于实际使用的UCS2,或者UCS4的BMP必然小于0x10000,所以就目前而言,可以认为UTF-16和UCS-2基本相同。但UCS-2只是一个编码方案,UTF-16却要用于实际的传输,所以就不得不考虑字节序的问题。 

    5、UTF的字节序和BOM 
    UTF-8以字节为编码单元,没有字节序的问题。UTF-16以两个字节为编码单元,在解释一个UTF-16文本前,首先要弄清楚每个编码单元的字节序。例如收到一个“奎”的Unicode编码是594E,“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”,那么这是“奎”还是“乙”? 
    Unicode规范中推荐的标记字节顺序的方法是BOM。BOM不是“Bill Of Material”的BOM表,而是Byte Order Mark。BOM是一个有点小聪明的想法: 
    在UCS编码中有一个叫做"ZERO WIDTH NO-BREAK SPACE"的字符,它的编码是FEFF。而FFFE在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输字符"ZERO WIDTH NO-BREAK SPACE"。 
    这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little-Endian的。因此字符"ZERO WIDTH NO-BREAK SPACE"又被称作BOM。 
    UTF-8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8编码是EF BB BF(读者可以用我们前面介绍的编码方法验证一下)。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。 Windows就是使用BOM来标记文本文件的编码方式的。 

猜你喜欢

转载自blog.csdn.net/ZytheMoon/article/details/79124066