您好,欢迎来到叨叨游戏网。
搜索
您的当前位置:首页转:Unicode字符集和多字节字符集关系

转:Unicode字符集和多字节字符集关系

来源:叨叨游戏网

unicode、ucs-2、ucs-4、utf-16、utf-32、utf-8

http://stallman.blogbus.com/logs/41709878.html

Unicode是为整合全世界的所有语言文字而诞生的。任何文字在Unicode中都对应一个值, 这个值称为代码点(code point)。代码点的值通常写成 U+ABCD 的格式。 而文字和代码点之间的对应关系就是UCS-2(Universal Character Set coded in 2 octets)。 顾名思义,UCS-2是用两个字节来表示代码点,其取值范围为 U+0000~U+FFFF。

为了能表示更多的文字,人们又提出了UCS-4,即用四个字节表示代码点。 它的范围为 U+00000000~U+7FFFFFFF,其中U+00000000~U+0000FFFF和UCS-2是一样的

 

要注意,UCS-2和UCS-4只规定了代码点和文字之间的对应关系,并没有规定代码点在计算机中如何存储。 规定存储方式的称为UTF(Unicode Transformation Format),其中应用较多的就是UTF-16和UTF-8了。

不难猜到,UTF-16是完全对应于UCS-2的,即把UCS-2规定的代码点通过Big Endian或Little Endian方式 直接保存下来。UTF-16包括三种:UTF-16,UTF-16BE(Big Endian),UTF-16LE(Little Endian)

UTF-16BE和UTF-16LE不难理解,而UTF-16就需要通过在文件开头以名为BOM(Byte Order Mark)的字符 来表明件是Big Endian还是Little Endian。

 

“ABC”这三个字符用各种方式编码后的结果如下:

UTF-16BE00 41 00 42 00 43
UTF-16LE41 00 42 00 43 00
UTF-16(Big Endian)FE FF 00 41 00 42 00 43
UTF-16(Little Endian)FF FE 41 00 42 00 43 00
UTF-16(不带BOM)00 41 00 42 00 43

Windows平台下默认的Unicode编码为Little Endian的UTF-16(即上述的 FF FE 41 00 42 00 43 00)。

UTF-16还能表示一部分的UCS-4代码点——U+10000~U+10FFFF。 表示算法比较复杂,简单说明如下:

UTF-32

UTF-32用四个字节表示代码点,这样就可以完全表示UCS-4的所有代码点,而无需像UTF-16那样使用复杂的算法。 与UTF-16类似,UTF-32也包括UTF-32、UTF-32BE、UTF-32LE三种编码,UTF-32也同样需要BOM字符。 仅用'ABC'举例:

UTF-32BE00 00 00 41 00 00 00 42 00 00 00 43
UTF-32LE41 00 00 00 42 00 00 00 43 00 00 00
UTF-32(Big Endian)00 00 FE FF 00 00 00 41 00 00 00 42 00 00 00 43
UTF-32(Little Endian)FF FE 00 00 41 00 00 00 42 00 00 00 43 00 00 00
UTF-32(不带BOM)00 00 00 41 00 00 00 42 00 00 00 43

UTF-8

UTF-16和UTF-32的一个缺点就是它们固定使用两个或四个字节, 这样在表示纯ASCII文件时会有很多00字节,造成浪费。 而定义的UTF-8则解决了这个问题。

UTF-8用1~4个字节来表示代码点。表示方式如下:

UCS-2 (UCS-4)位序列第一字节第二字节第三字节第四字节
U+0000 .. U+007F00000000-0xxxxxxx0xxxxxxx


U+0080 .. U+07FF 00000xxx-xxyyyyyy 110xxxxx 10yyyyyy

U+0800 .. U+FFFF xxxxyyyy-yyzzzzzz 1110xxxx 10yyyyyy 10zzzzzz
U+10000..U+1FFFFF 00000000-000wwwxx-
xxxxyyyy-yyzzzzzzz
11110www 10xxxxxx 10yyyyyy 10zzzzzz
* 0xxxxxxx                                                                (00-7f)
* 110xxxxx 10xxxxxx             (c0-df)(80-bf)
* 1110xxxx 10xxxxxx 10xxxxxx       (e0-ef)(80-bf)(80-bf)
* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx (f0-f7)(80-bf)(80-bf)(80-bf)
* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx (f8-fb)(80-bf)(80-bf)(80-bf)(80-bf)
* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx (fc-fd)(80-bf)(80-bf)(80-bf)(80-bf)(80-bf)

可见,ASCII字符(U+0000~U+007F)部分完全使用一个字节,避免了存储空间的浪费。 而且UTF-8不再需要BOM字节。

另外,从上表中可以看出,单字节编码的第一字节为[00-7F],双字节编码的第一字节为[C0-DF], 三字节编码的第一字节为[E0-EF]。这样只要看到第一个字节的范围就可以知道编码的字节数。 这样也可以大大简化算法。

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"的缩写

根据维基百科全书(http://zh.wikipedia.org/wiki/)的记载:历史上存在两个试图设计Unicode的组织,即国 际标准化组织(ISO)和一个软件制造商的协会(unicode.org)。ISO开发了ISO 106项目,Unicode协会开发了Unicode项目。

在1991年前后,双方都认识到世界不需要两个不兼容的字符集。于是它们开始合并双方的工作成果,并为创立一个单一编码表而协同工作。从 Unicode2.0开始,Unicode项目采用了与ISO 106-1相同的字库和字码。

目前两个项目仍都存在,并地公布各自的标准。Unicode协会现在的最新版本是2005年的Unicode 4.1.0。ISO的最新标准是ISO 106-3:2003。

UCS只是规定如何编码,并没有规定如何传输、保存这个编码。 例如“汉”字的UCS编码是6C49,我可以用4个ascii数字来传输、保存这个编 码;也可以用utf-8编码:3个连续的字节E6 B1 来表示它。关键在于通信双方都要认可。UTF-8、UTF-7、UTF-16都是被广泛接受的方案。UTF-8的一个特别的好处是它与ISO- 8859-1完全兼容。UTF是“UCS Transformation Format”的缩写。

IETF的RFC2781和RFC3629以RFC的一贯风格,清晰、明快又不失严谨地描述了UTF-16和UTF-8的编码方法。我总是记不得 IETF是Internet Engineering Task Force的缩写。但IETF负责维护的RFC是Internet上一切规范的基础。

2.1、内码和code page

目前Windows的内核已经支持Unicode字符集,这样在内核上可以支持全世界所有的语言文字。但是由于现有的大量程序和文档都采用了某种特 定语言的编码,例如GBK,Windows不可能不支持现有的编码,而全部改用Unicode。

Windows使用代码页(code page)来适应各个国家和地区。code page可以被理解为前面提到的内码。GBK对应的code page是CP936

微软也为GB18030定义了code page:CP54936。但是由于GB18030有一部分4字节编码,而Windows的代码页只支持单字节和双字节编码,所以这个code page是无法真正使用的。

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=21474838个码位。

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 - 007F0xxxxxxx
0080 - 07FF110xxxxx 10xxxxxx
0800 - FFFF1110xxxx 10xxxxxx 10xxxxxx

例如“汉”字的Unicode编码是6C49。6C49在0800-FFFF之间,所以肯定要用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是:0110 110001 001001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即 E6 B1 。

读者可以用记事本测试一下我们的编码是否正确。需要注意,UltraEdit在打开utf-8编码的文本文件时会自动转换为UTF-16,可能产生 混淆。你可以在设置中关掉这个选项。更好的工具是Hex Workshop。

UTF-16以16位为单元对UCS进行编码。对于小于0x10000的UCS码,UTF-16编码就等于UCS码对应的16位无符号整数。对于不 小于0x10000的UCS码,定义了一个算法。不过由于实际使用的UCS2,或者UCS4的BMP必然小于0x10000,所以目前而言,可以认为 UTF-16和UCS-2基本相同。但UCS-2只是一个编码方案,UTF-16却要用于实际的传输,所以就不得不考虑字节序的问题

 

 

谈谈Unicode编码,简要解释UCS、UTF、BMP、BOM等名词

这是一篇程序员写给程序员的趣味读物。所谓趣味是指可以比较轻松地了解一些原来不清楚的概念,增进知识,类似于打RPG游戏的升级。整理这篇文章的 动机是两个问题:

问题一:

使用Windows记事本的“另存为”,可以在GBK、Unicode、Unicode big endian和UTF-8这几种编码方式间相互转换。同样是txt文件,Windows是怎样识别编码方式的呢?

我很早前就发现Unicode、Unicode big endian和UTF-8编码的txt文件的开头会多出几个字节,分别是FF、FE(Unicode),FE、FF(Unicode big endian),EF、BB、BF(UTF-8)。但这些标记是基于什么标准呢?

问题二:
最近在网上看到一个ConvertUTF.c,实现了UTF-32、UTF-16和UTF-8这三种编码方式的相互转换。对于 Unicode(UCS2)、GBK、UTF-8这些编码方式,我原来就了解。但这个程序让我有些糊涂,想不起来UTF-16和UCS2有什么关系。

查了查相关资料,总算将这些问题弄清楚了,顺带也了解了一些Unicode的细节。写成一篇文章,送给有过类似疑问的朋友。本文在写作时尽量做到通 俗易懂,但要求读者知道什么是字节,什么是十六进制。

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称作“大尾”和“小尾”。

1、字符编码、内码,顺带介绍汉字编码

字符必须编码后才能被计算机处理。计算机使用的缺省编码方式就是计算机的内码。早期的计算机使用7位的ASCII编码,为了处理汉字,程序员设计了 用于简体中文的GB2312和用于繁体中文的big5。

GB2312(1980年)一共收录了7445个字符,包括6763个汉字和682个其它符号。汉字区的内码范围高字节从B0-F7,低字节从 A1-FE,占用的码位是72*94=6768。其中有5个空位是D7FA-D7FE。

GB2312支持的汉字太少。1995年的汉字扩展规范GBK1.0收录了21886个符号,它分为汉字区和图形符号区。汉字区包括21003个字 符。

从ASCII、GB2312到GBK,这些编码方法是向下兼容的,即同一个字符在这些方案中总是有相同的编码,后面的标准支持更多的字符。在这些 编 码中,英文和中文可以统一地处理。区分中文编码的方法是高字节的最高位不为0。按照程序员的称呼,GB2312、GBK都属于双字节字符集 (DBCS)。

2000年的GB18030是取代GBK1.0的正式国家标准。该标准收录了27484个汉字,同时还收录了藏文、蒙文、维吾尔文等主要的少数民 族 文字。从汉字字汇上说,GB18030在GB13000.1的20902个汉字的基础上增加了CJK扩展A的6582个汉字(Unicode码 0x3400-0x4db5),一共收录了27484个汉字。

CJK就是中日韩的意思。Unicode为了节省码位,将中日韩三国语言中的文字统一编码。GB13000.1就是ISO/IEC 106-1的中文版,相当于Unicode 1.1。

GB18030的编码采用单字节、双字节和4字节方案。其中单字节、双字节和GBK是完全兼容的。4字节编码的码位就是收录了CJK扩展A的 6582个汉字。 例如:UCS的0x3400在GB18030中的编码应该是8139EF30,UCS的0x3401在GB18030中的编码应该是8139EF31。

微软提供了GB18030的升级包,但这个升级包只是提供了一套支持CJK扩展A的6582个汉字的新字体:新宋体-18030,并不改变内码。 Windows 的内码仍然是GBK。

这里还有一些细节:

  • GB2312的原文还是区位码,从区位码到内码,需要在高字节和低字节上分别加上A0。

  • 对于任何字符编码,编码单元的顺序是由编码方案指定的,与endian无关。例如GBK的编码单元是字节,用两个字节表示一个汉字。 这两个字节的顺序是固定的,不受CPU字节序的影响。UTF-16的编码单元是word(双字节),word之间的顺序是编码方案指定的,word内部的 字节排列才会受到endian的影响。后面还会介绍UTF-16。

  • 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"的缩写。

根据维基百科全书(http://zh.wikipedia.org/wiki/)的记载:历史上存在两个试图设计Unicode的组织,即 国 际标准化组织(ISO)和一个软件制造商的协会(unicode.org)。ISO开发了ISO 106项目,Unicode协会开发了Unicode项目。

在1991年前后,双方都认识到世界不需要两个不兼容的字符集。于是它们开始合并双方的工作成果,并为创立一个单一编码表而协同工作。从 Unicode2.0开始,Unicode项目采用了与ISO 106-1相同的字库和字码。

目前两个项目仍都存在,并地公布各自的标准。Unicode协会现在的最新版本是2005年的Unicode 4.1.0。ISO的最新标准是ISO 106-3:2003。

UCS只是规定如何编码,并没有规定如何传输、保存这个编码。例如“汉”字的UCS编码是6C49,我可以用4个ascii数字来传输、保存这个 编 码;也可以用utf-8编码:3个连续的字节E6 B1 来表示它。关键在于通信双方都要认可。UTF-8、UTF-7、UTF-16都是被广泛接受的方案。UTF-8的一个特别的好处是它与ISO- 8859-1完全兼容。UTF是“UCS Transformation Format”的缩写。

IETF的RFC2781和RFC3629以RFC的一贯风格,清晰、明快又不失严谨地描述了UTF-16和UTF-8的编码方法。我总是记不得 IETF是Internet Engineering Task Force的缩写。但IETF负责维护的RFC是Internet上一切规范的基础。

2.1、内码和code page

目前Windows的内核已经支持Unicode字符集,这样在内核上可以支持全世界所有的语言文字。但是由于现有的大量程序和文档都采用了某种特 定语言的编码,例如GBK,Windows不可能不支持现有的编码,而全部改用Unicode。

Windows使用代码页(code page)来适应各个国家和地区。code page可以被理解为前面提到的内码。GBK对应的code page是CP936。

微软也为GB18030定义了code page:CP54936。但是由于GB18030有一部分4字节编码,而Windows的代码页只支持单字节和双字节编码,所以这个code page是无法真正使用的。

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=21474838个码位。

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 - 007F0xxxxxxx
0080 - 07FF110xxxxx 10xxxxxx
0800 - FFFF1110xxxx 10xxxxxx 10xxxxxx

例如“汉”字的Unicode编码是6C49。6C49在0800-FFFF之间,所以肯定要用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是:0110 110001 001001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即 E6 B1 。

读者可以用记事本测试一下我们的编码是否正确。需要注意,UltraEdit在打开utf-8编码的文本文件时会自动转换为UTF-16,可能产生 混淆。你可以在设置中关掉这个选项。更好的工具是Hex Workshop。

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 MarkBOM是一个有点小聪明的想法:

在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来标记文本文件的编码方式的。

 

 

“GB2312的原文”是指国家1980年的一个标准《中华人民共和国国家标准 信息交换用汉字编码字符集 基本集 GB 2312-80》。这个标准用两个数来编码汉字和中文符号。第一个数称为“区”,第二个数称为“位”。所以也称为区位码。1-9区是中文符号,16-55 区是一级汉字,56-87区是二级汉字。现在Windows也还有区位输入法,例如输入1601得到“啊”。(这个区位输入法可以自动识别16进制的 GB2312和10进制的区位码,也就是说输入B0A1同样会得到“啊”。)

内码是指操作系统内部的字符编码。早期操作系统的内码是与语言相关的。现在的Windows在系统内部支持Unicode,然后用代码页适应各种语 言,“内码”的概念就比较模糊了。微软一般将缺省代码页指定的编码说成是内码。

内码这个词汇,并没有什么官方的定义,代码页也只是微软这个公司的叫法。作为程序员,我们只要知道它们是什么东西,没有必要过多地考证这些名词。

Windows中有缺省代码页的概念,即缺省用什么编码来解释字符。例如Windows的记事本打开了一个文本文件,里面的内容是字节流:BA、 BA、D7、D6。Windows应该去怎么解释它呢?

是按照Unicode编码解释、还是按照GBK解释、还是按照BIG5解释,还是按照ISO8859-1去解释?如果按GBK去解释,就会得到 “汉 字”两个字。按照其它编码解释,可能找不到对应的字符,也可能找到错误的字符。所谓“错误”是指与文本作者的本意不符,这时就产生了乱码。

答案是Windows按照当前的缺省代码页去解释文本文件里的字节流。缺省代码页可以通过控制面板的区域选项设置。记事本的另存为中有一项 ANSI,其实就是按照缺省代码页的编码方法保存。

Windows的内码是Unicode,它在技术上可以同时支持多个代码页。只要文件能说明自己使用什么编码,用户又安装了对应的代码 页,Windows就能正确显示,例如在HTML文件中就可以指定charset。

有的HTML文件作者,特别是英文作者,认为世界上所有人都使用英文,在文件中不指定charset。如果他使用了0x80-0xff之间的字符, 中文Windows又按照缺省的GBK去解释,就会出现乱码。这时只要在这个html文件中加上指定charset的语句,例如:
<meta http-equiv="Content-Type" content="text/html; charset=ISO8859-1">
如 果原作者使用的代码页和ISO8859-1兼容,就不会出现乱码了。

再说区位码,啊的区位码是1601,写成16进制是0x10,0x01。这和计算机广泛使用的ASCII编码冲突。为了兼容00-7f的 ASCII 编码,我们在区位码的高、低字节上分别加上A0。这样“啊”的编码就成为B0A1。我们将加过两个A0的编码也称为GB2312编码,虽然GB2312的 原文根本没提到这一点。

*****************************************************************************

 

Unicode字符集和多字节字符集关系

#####################################################################

 

ANSI字符,UNICODE,宽字符,窄字符,多字节字符集

10. 如何对字符串进行有选择的比较?
通过调用CompareString来实现。
标志 含义
NORM_IGNORECASE 忽略字母的大小写
NORM_IGNOREKANATYPE 不区分平假名与片假名字符
NORM_IGNORENONSPACE 忽略无间隔字符
NORM_IGNORESYMBOLS 忽略符号
NORM_IGNOREWIDTH 不区分单字节字符与作为双字节字符的同一个字符
SORT_STRINGSORT 将标点符号作为普通符号来处理
11. 如何判断一个文本文件是ANSI还是Unicode?
判断如果文本文件的开头两个字节是0xFF和0xFE,那么就是Unicode,否则是ANSI。
12. 如何判断一段字符串是ANSI还是Unicode?
用IsTextUnicode进行判断。IsTextUnicode使用一系列统计方法和定性方法,以便猜测缓存的内容。由于这不是一种确切的科学方法,因此 IsTextUnicode有可能返回不正确的结果。
13. 如何在Unicode与ANSI之间转换字符串?
Windows函数MultiByteToWideChar用于将多字节字符串转换成宽字符串;函数WideCharToMultiByte将宽字符串转换成等价的多字节字符串。

  

________________________________________________________________

UCS,UNICODE和UTF-8

UCS,UNICODE和UTF-8                                     

  本文简单介绍UCS,UNICODE和UTF-8,并利用C语言实现了UTF-8与UCS2之间的互相转化。
1.什么是UCS和ISO106?
国 际标准ISO106定义了通用字符集(Universal Character Set, UCS). UCS是所有其它字符集标准的一个超集,它保证也其它字符集双向兼容,即编码间相互转换不会丢失任何信息。UCS字符集U+0000到U+007F与 US-ASCII是一致的。
2.什么是UNICODE
历史上, 有两个的, 创立单一字符集的尝试. 一个是国际标准化组织(ISO)的 ISO 106 项目, 另一个是由(一开始大多是美国的)多语言软件制造商组成的协会组织的 Unicode 项目. 幸运的是, 1991年前后, 两个项目的参与者都认识到, 世界不 需要两个不同的单一字符集. 它们合并双方的工作成果, 并为创立一个单一编码表而协同工作. 两个项目仍都存在并地公布各自的标准, 但 Unicode 协会和 ISO/IEC JTC1/SC2 都同意保持 Unicode 和 ISO 106 标准的码表兼容, 并紧密地共同调整任何未来的扩展.
3.什么是UTF-8(一种传送和存储格式)
UCS和UNICODE为每个字符分配了一个对应的整数,但并没有明确说明其实现机制.故存在多种编码方式,其中以两个字节和四个字节来存储一个字符的方法分别叫UCS-2, UCS-4,要将一个ASCII文件转换成一个UCS-2文件只要在每个字节前加一个字节0X00,转换成UCS-4只要在每个字节前加三个0X00。
internet上大量的信息是以ASCII码存在的,如果都用两个字节来存储将浪费大量的资源,同时Unix和Linux下使用USC-2和USC-4会导致严重问题,于是出现了UTF-8(定义于ISO106-1).
UTF-8

(UTF-8 stands for Unicode Transformation Format-8. It is an octet (8-bit) lossless encoding of Unicode characters.)
UNICODE(UCS)和UTF-8的对应关系
U-00000000 - U-0000007F:  0xxxxxxx   (ASCII码 最重用)
U-00000080 - U-000007FF:  110xxxxx 10xxxxxx   (第二优先级常用)
U-00000800 - U-0000FFFF:  1110xxxx 10xxxxxx 10xxxxxx  
U-00010000 - U-001FFFFF:  11110xxx 10xxxxxx 10xxxxxx 10xxxxxx  
U-00200000 - U-03FFFFFF:  111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx  
U-04000000 - U-7FFFFFFF:  1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx  (很少用)
在多字节串中,第一个字节的开头‘1’的数目就是整个串中字节的数目.
下面举UCS-2与UTF-8的对应关系,并利用C语言实现之间的互相转化。
-----------------------------------------------------------------------------------------------------------
| UCS2                               | UTF-8                                  |
|----------------------------------------------------------------------------------------------------------
|                                         |    code           | 1st Byte       | 2nd byte       | 3rd Byte       |
|--------------------------------------------------------------------------------------------------------
| 000000000aaaaaaa    | 0000 - 007F  | 0aaaaaaa  |                        |                      |
|--------------------------------------------------------------------------------------------------------
| 00000bbbbbaaaaaa    | 0080 - 07FF  | 110bbbbb  | 10aaaaaa    |                       |
|--------------------------------------------------------------------------------------------------------
| ccccbbbbbbaaaaaa     | 0800 - FFFF  | 1110cccc   | 10bbbbbb     | 10aaaaaa  |
|--------------------------------------------------------------------------------------------------------

alphajay的疑问: 这里是a b c应该是从0 1中取的一个bit位吧

                                      因为USC-2 每个字符用两个字节 16bit表示


在这儿我只实现了单个字符的转换,串的转换也是一样的道理。
1,把一个UTF-8字符转换成一个UCS-2字符。
如果转换成功返回1,如果UTF-8字符是一个unrecognized字符,则返回0,且存一个blackbox(U+22e0)到ucs2_code_ptr中。

typedef unsigned short UINT16;
typedef unsigned char UINT8;
typedef unsigned char BOOL;
#define TRURE (BOOL)(1)
#define FALSE (BOOL)(0)
BOOL UTF8toUCS2Code(const UINT8* utf8_code, UINT16* ucs2_code){
       UINT16 temp1, temp2;
       BOOL is_unrecognized = FALSE ;
       UINT16 * in = utf8_code;
       if(!utf8_code || !ucs2_code){
             return is_unrecognized;
      }
      
      if(0x00 == (*in & 0x80)){
      /* 1 byte UTF-8 Charater.*/
             *ucs2_code= (UINT16)*in;
             is_unrecognized = TRUE;
      }
      else if(0xc0 == (*in & 0xe0) &&
               0x80 == (*(in + 1) & 0xc0)
              ){
      /* 2 bytes UTF-8 Charater.*/  
             temp1 = (UINT16)(*in & 0x1f);
             temp1 <<= 6;
             temp1 |= (UINT16)(*(in + 1) & 0x3f);
             *ucs2_code = temp1;
             is_unrecognized = TRUE;
      }
      else if( 0xe0 == (*in & 0xf0) &&
                0x80 == (*(in +1) & 0xc0) &&
                0x80 == (*(in + 2) & 0xc0)
              ){
      /* 3bytes UTF-8 Charater.*/
             temp1 = (UINT16)(*in &0x0f);
             temp1 <<= 12;
             temp2 = (UINT16)(*(in+1) & 0x3F);
             temp2 <<= 6;
             temp1 = temp1 | temp2 | (UINT16)(*(in+2) & 0x3F);
             *ucs2_code = temp1;
             is_unrecognized  = TRUE;
       }
       else{
       /* unrecognize byte. */
             *ucs2_code = 0x22e0;
             is_unrecognized = FALSE;
     }
     return is_unrecognized;
}
2,把一个UCS-2字符转换成UTF-8字符。函数返回转换成UTF-8的长度(字节1 -- 3),如果目标指针为空,返回0。
UINT8 UCS2toUTF8Code(UINT16 ucs2_code, UINT8* utf8_code){
      int length = 0;
      UINT8* out = utf8_code;
      if(!utf8_code){
           return length;
      }
      if(0x0080 > ucs2_code){
      /* 1 byte UTF-8 Character.*/
          *out = (UINT8)ucs2_code;
          length++;   
      }
      else if(0x0800 > ucs2_code){
      /*2 bytes UTF-8 Character.*/
           *out = ((UINT8)(ucs2_code >> 6)) | 0xc0;
           *(out+1) = ((UINT8)(ucs2_code & 0x003F)) | 0x80;
           length += 2;
     }
     else{
     /* 3 bytes UTF-8 Character .*/
        *out = ((UINT8)(ucs2_code >> 12)) | 0xE0;
        *(out+1) = ((UINT8)((ucs2_code & 0x0FC0)>> 6)) | 0x80;
        *(out+2) = ((UINT8)(ucs2_code & 0x003F)) | 0x80;
        length += 3;               
    }
    return length;
}
字符串间的转换也是一样的。

  

[概述]
      计算机中的一切都是以数字来表示的,字符同样如此。字符编码就是将字符集编码成为数字序列,以便能让计算机识别。各个地区和国家使用的语言有别,将本地 使用的语言符号进行编码就得到本地编码字符集。例如西欧国家使用的本地编码是ISO8859-1,中国和新加坡等地区使用本地编码是GB2312或 GBK,中国港台地区使用的本地编码是BIG5,韩国和日本的本地编码分别是euc-kr和Shift_JIS。电脑的操作系统支持各种本地编码字符集, 操作系统默认的本地编码和你所安装的操作系统语言版本是一致的。本地集只对本地使用的文字符号进行了编码,并不包括其他地区使用的文字,即使两个本地集中 包含了相同的字符,这个字符的编码值也是不同的。例如“中”的GB2312或GBK编码值为“0xD6D0”,而BIG5编码值为“0xA4A4”。
      全球信息交流与融合的趋势要求实现对本地字符集的统一,1984年4月ISO成立了工作组,针对各国文字、符号进行统一编码,这种编码成为 Unicode。Unicode于1992年6月通过DIS(DrafInternationalStandard),V2.0版本于1996年发布。 Unicode编码包括了符号6811个、汉字20902个、韩文11172个、等等。Unicode虽然实现了全球统一编码,但是在字符集数量和编码效 率方面显然存在着不足,而UTF-8、UTF-16就是针对Unicode编码进行转换或扩充形成的编码,UTF是Unicode Translation Format的缩写。


[细节]
关于ASCII编码
      ASCII编码是美国标准信息交换码,这种编码方式针对的是英文字符。 ASCII编码使用一个字节对字符进行编码,而且字节的最高位都为0,因此ASCII编码的字符集大小是128个。由于英文字母仅有26个,再加上其他一 些常用符号,总大小也不会超过128个,因此ASCII编码的空间是足够的。例如,字符“a”被编码为0x61,字符“b”被编码为0x62等等。注意, 在有的时候ASCII泛指本地编码,例如文本编辑器UltraEdit中有诸如“ASCII转Unicode”的功能,这里的ASCII就泛指本地编码, 如果本地编码是GBK,这个功能执行的就是GBK编码到Unicode编码的转换。

关于ISO8859-1编码
      ISO8859-1是西欧语系国家通用的字符集编码,ISO8859-1使用一个字节对字符进行编码,编码值范围是0x00-0xFF。其 中,0x00-0x1F用作控制字,0x20-0x7F表示字母、数字和符号这些图形字符,0xA0-0xFF作为附加部分使用。由于ASCII编码只使 用了一个字节中的低7位,编码范围仅为0-127,虽然可以容纳英文字符和其他的一些符号,但是却不能包含除英文以外的其他西欧语言的字母,因此 ASCII编码在西欧国家并不通用。针对这个问题ISO在ASCII编码的基础上进行了扩充,制定了ISO8859-1编码,ISO8859-1编码使用 了一个字节的全部8位,编码范围是0-255,能包含西欧语系的所有字母和符号。

关于GB2312、GBK和BIG5编码
      GB2312码是中华人民共和国国家汉字信息交换使用码,全称《信息交换使用汉字编码字符集-基本集》,由国家标准总局发布,1981年5月1日实施,中国和新加坡等地使用此编码。GB2312收录了简化汉字、符号、字母、日文假名等共计7445个字符,其中汉字占6763个。GB2312将代码表分区94个区(0xA1-0xFE),对应第一个字节,每个区94个位(0xA1-0xFE),对应了第二字节,两个字节的值分别为区号的值和位号的值加32(0x20),因此也被称为区位码。GB2312的编码范7围是0x2121-0x777E,与ASCII有重叠,通常方法是将GB码的两个字节的最高位置1区别
      GBK是GB2312-80的扩展,向上兼容, 包含了20902个汉字,编码范围是0x8140-0xFEFE,剔除高位0x80的字位,其他字符都可以一一映射到Unicode2.0。 GB18030-2000(GBK2K)在GBK的基础上增加了藏、蒙等少数民族的字符,GBK2K从根本上解决了字位不够、字形不足的问题。GBK2K 首先要求实现能够完全映射到Unicode3.0标准的所有字形,现在还没有任何一个操作系统支持GBK2K。
      BIG5码被称为大五码,是中国港台地区使用的字符编码方式。TW-BIG5码将所有字分为两大群,即常用字区和次常用字区,每个字区分都采用笔画排序,同笔画的字依部首排序。TW-BIG5每个字由两个字节组成

转载于:https://www.cnblogs.com/dhf-0214/p/3517120.html

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- gamedaodao.net 版权所有 湘ICP备2024080961号-6

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务