http://www.regexlab.com/zh/encoding.htm
1. 编码问题的由来,相关概念的理解
1.1 字符与编码的发展
从计算机对多国语言的支持角度看,大致可以分为三个阶段:
系统内码 | 说明 | 系统 | |
阶段一 | ASCII | 计算机刚开始只支持英语,其它语言不能够在计算机上存储和显示。 | 英文 DOS |
阶段二 | ANSI编码 (本地化) |
为使计算机支持更多语言,通常使用 0x80~0xFF 范围的 2 个字节来表示 1 个字符。比如:汉字 '中' 在中文操作系统中,使用 [0xD6,0xD0] 这两个字节存储。 不同的国家和地区制定了不同的标准,由此产生了 GB2312, BIG5, JIS 等各自的编码标准。这些使用 2 个字节来代表一个字符的各种汉字延伸编码方式,称为 ANSI 编码。在简体中文系统下,ANSI 编码代表 GB2312 编码,在日文操作系统下,ANSI 编码代表 JIS 编码。 不同 ANSI 编码之间互不兼容,当信息在国际间交流时,无法将属于两种语言的文字,存储在同一段 ANSI 编码的文本中。 |
中文 DOS,中文 Windows 95/98,日文 Windows 95/98 |
阶段三 | UNICODE (国际化) |
为了使国际间信息交流更加方便,国际组织制定了 UNICODE 字符集,为各种语言中的每一个字符设定了统一并且唯一的数字编号,以满足跨语言、跨平台进行文本转换、处理的要求。 | Windows NT/2000/XP,Linux,Java |
字符串在内存中的存放方法:
在 ASCII 阶段,单字节字符串使用一个字节存放一个字符(SBCS)。比如,"Bob123" 在内存中为:
42 6F 62 31 32 33 00 B o b 1 2 3 \0
在使用 ANSI 编码支持多种语言阶段,每个字符使用一个字节或多个字节来表示(MBCS),因此,这种方式存放的字符也被称作多字节字符。比如,"中文123" 在中文 Windows 95 内存中为7个字节,每个汉字占2个字节,每个英文和数字字符占1个字节:
D6 D0 CE C4 31 32 33 00 中 文 1 2 3 \0
在 UNICODE 被采用之后,计算机存放字符串时,改为存放每个字符在 UNICODE 字符集中的序号。目前计算机一般使用 2 个字节(16 位)来存放一个序号(DBCS),因此,这种方式存放的字符也被称作宽字节字符。比如,字符串 "中文123" 在 Windows 2000 下,内存中实际存放的是 5 个序号, 一共占 10 个字节:
2D 4E 87 65 31 00 32 00 33 00 00 00 // 在 x86 CPU 中,低字节在前 中 文 1 2 3 \0
1.2 字符,字节,字符串
理解编码的关键,是要把字符的概念和字节的概念理解准确。这两个概念容易混淆,我们在此做一下区分:
概念描述 | 举例 | |
字符 | 人们使用的记号,抽象意义上的一个符号。 | '1', '中', 'a', '$', '¥' |
字节 | 计算机中存储数据的单元,一个8位的二进制数,是一个很具体的存储空间。 | 0x01, 0x45, 0xFA |
ANSI 字符串 |
在内存中,如果“字符”是以 ANSI 编码形式存在的,一个字符可能使用一个字节或多个字节来表示,那么我们称这种字符串为 ANSI 字符串或者多字节字符串。 | "中文123" (占7字节) |
UNICODE 字符串 |
在内存中,如果“字符”是以在 UNICODE 中的序号存在的,那么我们称这种字符串为 UNICODE 字符串或者宽字节字符串。 | L"中文123" (占10字节) |
由于不同 ANSI 编码所规定的标准是不相同的,因此,对于一个给定的多字节字符串,我们必须知道它采用的是哪一种编码规则,才能够知道它包含了哪些“字符”。而对于 UNICODE 字符串来说,不管在什么环境下,它所代表的“字符”内容总是不变的。
1.3 字符集与编码
使用哪些字符。也就是说哪些汉字,字母和符号会被收入标准中。所包含“字符”的集合就叫做“字符集”。各个国家和地区所制定的不同 ANSI 编码标准中,都只规定了各自语言所需的“字符”。比如:汉字标准(GB2312)中没有规定韩国语字符怎样存储。这些 ANSI 编码标准所规定的内容包含两层含义:
- 使用哪些字符。也就是说哪些汉字,字母和符号会被收入标准中。所包含“字符”的集合就叫做“字符集”。
- 规定每个“字符”分别用一个字节还是多个字节存储,用哪些字节来存储,这个规定就叫做“编码”。
各个国家和地区在制定编码标准的时候,“字符的集合”和“编码”一般都是同时制定的。
因此,平常我们所说的“字符集”,比如:GB2312, GBK, JIS 等,除了有“字符的集合”这层含义外,同时也包含了“编码”的含义。
“UNICODE 字符集”包含了各种语言中使用到的所有“字符”。
用来给 UNICODE 字符集编码的标准有很多种,比如:UTF-8, UTF-7, UTF-16, UnicodeLittle, UnicodeBig 等。
1.4 常用的编码简介
简单介绍一下常用的编码规则,为后边的章节做一个准备。在这里,我们根据编码规则的特点,把所有的编码分成三类
分类 | 编码标准 | 说明 |
单字节字符编码 | ISO-8859-1 | 最简单的编码规则,每一个字节直接作为一个 UNICODE 字符。比如,[0xD6, 0xD0] 这两个字节,通过 iso-8859-1 转化为字符串时,将直接得到 [0x00D6, 0x00D0] 两个 UNICODE 字符,即 "ÖÐ"。 反之,将 UNICODE 字符串通过 iso-8859-1 转化为字节串时,只能正常转化 0~255 范围的字符。 |
ANSI 编码 | GB2312,BIG5,Shift_JIS, ISO-8859-2 |
把 UNICODE 字符串通过 ANSI 编码转化为“字节串”时,根据各自编码的规定,一个 UNICODE 字符可能转化成一个字节或多个字节。 反之,将字节串转化成字符串时,也可能多个字节转化成一个字符。比如,[0xD6, 0xD0] 这两个字节,通过 GB2312 转化为字符串时,将得到 [0x4E2D] 一个字符,即 '中' 字。 “ANSI 编码”的特点: 1. 这些“ANSI 编码标准”都只能处理各自语言范围之内的 UNICODE 字符。 2. “UNICODE 字符”与“转换出来的字节”之间的关系是人为规定的。 |
UNICODE 编码 | UTF-8,UTF-16, UnicodeBig | 与“ANSI 编码”类似的,把字符串通过 UNICODE 编码转化成“字节串”时,一个 UNICODE 字符可能转化成一个字节或多个字节。 与“ANSI 编码”不同的是: 1. 这些“UNICODE 编码”能够处理所有的 UNICODE 字符。 2. “UNICODE 字符”与“转换出来的字节”之间是可以通过计算得到的。 |
我们实际上没有必要去深究每一种编码具体把某一个字符编码成了哪几个字节,我们只需要知道“编码”的概念就是把“字符”转化成“字节”就可以了。
对于“UNICODE 编码”,由于它们是可以通过计算得到的,因此,在特殊的场合,我们可以去了解某一种“UNICODE 编码”是怎样的规则。
2. 字符与编码在程序中的实现
2.1 程序中的字符与字节
在 C++ 和 Java 中,用来代表“字符”和“字节”的数据类型,以及进行编码的方法:
类型或操作 | C++ | Java |
字符 | wchar_t | char |
字节 | char | byte |
ANSI 字符串 | char[] | byte[] |
UNICODE 字符串 | wchar_t[] | String |
字节串→字符串 | mbstowcs(), MultiByteToWideChar() | string = new String(bytes, "encoding") |
字符串→字节串 | wcstombs(), WideCharToMultiByte() | bytes = string.getBytes("encoding") |
以上需要注意几点:
- Java 中的 char 代表一个“UNICODE 字符(宽字节字符)”,而 C++ 中的 char 代表一个字节。
- MultiByteToWideChar() 和 WideCharToMultiByte() 是 Windows API 函数。
2.2 C++ 中相关实现方法
声明一段字符串常量:
// ANSI 字符串,内容长度 7 字节 char sz[20] = "中文123"; // UNICODE 字符串,内容长度 5 个 wchar_t(10 字节) wchar_t wsz[20] = L"\x4E2D\x6587\x0031\x0032\x0033";
UNICODE 字符串的 I/O 操作,字符与字节的转换操作:
// 运行时设定当前 ANSI 编码,VC 格式 setlocale(LC_ALL, ".936"); // GCC 中格式 setlocale(LC_ALL, "zh_CN.GBK"); // Visual C++ 中使用小写 %s,按照 setlocale 指定编码输出到文件 // GCC 中使用大写 %S fwprintf(fp, L"%s\n", wsz); // 把 UNICODE 字符串按照 setlocale 指定的编码转换成字节 wcstombs(sz, wsz, 20); // 把字节串按照 setlocale 指定的编码转换成 UNICODE 字符串 mbstowcs(wsz, sz, 20);
在 Visual C++ 中,UNICODE 字符串常量有更简单的表示方法。如果源程序的编码与当前默认 ANSI 编码不符,则需要使用 #pragma setlocale,告诉编译器源程序使用的编码:
// 如果源程序的编码与当前默认 ANSI 编码不一致, // 则需要此行,编译时用来指明当前源程序使用的编码 #pragma setlocale(".936") // UNICODE 字符串常量,内容长度 10 字节 wchar_t wsz[20] = L"中文123";
以上需要注意 #pragma setlocale 与 setlocale(LC_ALL, "") 的作用是不同的,#pragma setlocale 在编译时起作用,setlocale() 在运行时起作用。
2.3 Java 中相关实现方法
字符串类 String 中的内容是 UNICODE 字符串:
// Java 代码,直接写中文 String string = "中文123"; // 得到长度为 5,因为是 5 个字符 System.out.println(string.length());
字符串 I/O 操作,字符与字节转换操作。在 Java 包 java.io.* 中,以“Stream”结尾的类一般是用来操作“字节串”的类,以“Reader”,“Writer”结尾的类一般是用来操作“字符串”的类。
// 字符串与字节串间相互转化 // 按照 GB2312 得到字节(得到多字节字符串) byte [] bytes = string.getBytes("GB2312"); // 从字节按照 GB2312 得到 UNICODE 字符串 string = new String(bytes, "GB2312"); // 要将 String 按照某种编码写入文本文件,有两种方法: // 第一种办法:用 Stream 类写入已经按照指定编码转化好的字节串 OutputStream os = new FileOutputStream("1.txt"); os.write(bytes); os.close(); // 第二种办法:构造指定编码的 Writer 来写入字符串 Writer ow = new OutputStreamWriter(new FileOutputStream("2.txt"), "GB2312"); ow.write(string); ow.close(); /* 最后得到的 1.txt 和 2.txt 都是 7 个字节 */
如果 java 的源程序编码与当前默认 ANSI 编码不符,则在编译的时候,需要指明一下源程序的编码。比如:
E:\>javac -encoding BIG5 Hello.java
以上需要注意区分源程序的编码与 I/O 操作的编码,前者是在编译时起作用,后者是在运行时起作用。
3. 几种误解,以及乱码产生的原因和解决办法
3.1 容易产生的误解
对编码的误解 | |
误解一 | 在将“字节串”转化成“UNICODE 字符串”时,比如在读取文本文件时,或者通过网络传输文本时,容易将“字节串”简单地作为单字节字符串,采用每“一个字节”就是“一个字符”的方法进行转化。 而实际上,在非英文的环境中,应该将“字节串”作为 ANSI 字符串,采用适当的编码来得到 UNICODE 字符串,有可能“多个字节”才能得到“一个字符”。 通常,一直在英文环境下做开发的程序员们,容易有这种误解。 |
误解二 | 在 DOS,Windows 98 等非 UNICODE 环境下,字符串都是以 ANSI 编码的字节形式存在的。这种以字节形式存在的字符串,必须知道是哪种编码才能被正确地使用。这使我们形成了一个惯性思维:“字符串的编码”。 当 UNICODE 被支持后,Java 中的 String 是以字符的“序号”来存储的,不是以“某种编码的字节”来存储的,因此已经不存在“字符串的编码”这个概念了。只有在“字符串”与“字节串”转化时, 或者,将一个“字节串”当成一个 ANSI 字符串时,才有编码的概念。不少的人都有这个误解。 |
第一种误解,往往是导致乱码产生的原因。第二种误解,往往导致本来容易纠正的乱码问题变得更复杂。
在这里,我们可以看到,其中所讲的“误解一”,即采用每“一个字节”就是“一个字符”的转化方法,实际上也就等同于采用 iso-8859-1 进行转化。因此,我们常常使用 bytes = string.getBytes("iso-8859-1") 来进行逆向操作,得到原始的“字节串”。然后再使用正确的 ANSI 编码,比如 string = new String(bytes, "GB2312"),来得到正确的“UNICODE 字符串”。
3.2 非 UNICODE 程序在不同语言环境间移植时的乱码
非 UNICODE 程序中的字符串,都是以某种 ANSI 编码形式存在的。如果程序运行时的语言环境与开发时的语言环境不同,将会导致 ANSI 字符串的显示失败。
比如,在日文环境下开发的非 UNICODE 的日文程序界面,拿到中文环境下运行时,界面上将显示乱码。如果这个日文程序界面改为采用 UNICODE 来记录字符串,那么当在中文环境下运行时,界面上将可以显示正常的日文。
由于客观原因,有时候我们必须在中文操作系统下运行非 UNICODE 的日文软件,这时我们可以采用一些工具,比如,南极星,AppLocale 等,暂时的模拟不同的语言环境。
4. 几种错误理解的纠正
误解:“ISO-8859-1 是国际编码?”
非也。iso-8859-1 只是单字节字符集中最简单的一种,也就是“字节编号”与“UNICODE 字符编号”一致的那种编码规则。
当我们要把一个“字节串”转化成“字符串”,而又不知道它是哪一种 ANSI 编码时,先暂时地把“每一个字节”作为“一个字符”进行转化,不会造成信息丢失。
然后再使用 bytes = string.getBytes("iso-8859-1") 的方法可恢复到原始的字节串。
误解:“Java 中,怎样知道某个字符串的内码?”
Java 中,字符串类 java.lang.String 处理的是 UNICODE 字符串,不是 ANSI 字符串。
我们只需要把字符串作为“抽象的符号的串”来看待。因此不存在字符串的内码的问题。
http://swingalone.blog.sohu.com/40580793.html
ASCII码, DBCS码,SBCS码 与Unicode码
对于英文来说,ascii码 0-127就足以代码所有字符,对于中文而言,则必须使用两个字节(byte)来代表一个字符,
具第一个字节必须大于127(所以我们有许程序判断中文都是以ascii码大于127作为条件)
以上用两个字节来表示一个中文的方式,在习惯上称为双字节(即DBCS: Double-Byte Character Set),而相对之下,
英文的字符码就称为单字节SBCS(Single-Byte Character Set)。
虽然双字节(DBCS)足以解决中英文字符混合使用情况,但对于不同字符系统而言,必须经过字符码转换,非常麻烦。例如:中英文混合情况,日文,韩文等等。
为解决这个问题,Apple, Xerox, Microsoft, IBM, Novell, Borland...很多公司联合起来制订了一套可以适用于全世界所有国家的字符码,就称为Unicode
java的内核就是16位双字节编码Unicode为基准
char类型表示Unicode编码方案中的字符。
Unicode可同时包含65536个字符,ASCII/ANSI只包含255个字符,实际上是Unicode的一个子集。
Unicode字符通常用十六进制编码方案表示,范围在'\u0000'到'\uFFFF'之间。\u0000到\u00FF表示ASCII/ANSI字符。\u表示这是一个Unicode值。
在Java中除了用这个\u的形式来表示字符外,还可以使用换码序列来表示特殊字符。
\b 退格 \u0008
\t Tab制表 \u0009
\n 换行 \u000a
\r 硬回车 \u000d
\" 双引号 \u0022
\' 单引号 \u0027
\\ 反斜杠 \u005c
Unicode是一种字符集,它有多种编码方式UTF-32,UTF-16,UTF-8,UTF-7等等。一个Unicode字符一般占用两个字节。ASCII(DBCS)是混合编码的,0x00-0x7f是传统的ASCII码,如果一个字节的值是0x80-0xff表示,它与后面的一个字节组成一个编码单位。
目前计算机中用得最广泛的字符集及其编码,是由美国国家标准局(ANSI)制定的ASCII码(American Standard Code for Information Interchange,美国标准信息交换码),它已被国际标准化组织(ISO)定为国际标准,称为ISO 646标准。适用于所有拉丁文字字母,ASCII码有7位码和8位码两种形式。
第0~32号及第127号(共34个)是控制字符或通讯专用字符,如控制符:LF(换行)、CR(回车)、FF(换页)、DEL(删除)、BEL(振铃)等; 通讯专用字符:SOH(文头)、EOT(文尾)、ACK(确认)等;
第33~126号(共94个)是字符,其中第48~57号为0~9十个阿拉伯数字;65~90号为26个大写英文字母,97~122号为26个小写英文字母,其余为一些标点符号、运算符号等。
Unicode码:Unicode码也是一种国际标准编码,采用二个字节编码,与ANSI码不兼容。目前,在网络、Windows系统和很多大型软件中得到应用。
Unicode(统一码、万国码、单一码)是一种在计算机上使用的字符编码。它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。1990年开始研发,1994年正式公布。随着计算机工作能力的增强,Unicode也在面世以来的十多年里得到普及。
Unicode 的编码和实现
大概来说,Unicode 编码系统可分为编码方式和实现方式两个层次。
1.编码方式
Unicode 的编码方式与 ISO 10646 的通用字元集(亦称[通用字符集])(Universal Character Set,UCS)概念相对应,目前的用于实用的 Unicode 版本对应于 UCS-2,使用16位的编码空间。也就是每个字符占用2个字节。这样理论上一共最多可以表示 216 个字符。基本满足各种语言的使用。实际上目前版本的 Unicode 尚未填充满这16位编码,保留了大量空间作为特殊使用或将来扩展。
上述16位 Unicode 字符构成基本多文种平面(Basic Multilingual Plane, 简称 BMP)。最新(但未实际广泛使用)的 Unicode 版本定义了16个辅助平面,两者合起来至少需要占据21位的编码空间,比3字节略少。但事实上辅助平面字符仍然占用4字节编码空间,与 UCS-4 保持一致。未来版本会扩充到 ISO 10646-1 实现级别3,即涵盖 UCS-4 的所有字符。UCS-4 是一个更大的尚未填充完全的31位字符集,加上恒为0的首位,共需占据32位,即4字节。理论上最多能表示 231 个字符,完全可以涵盖一切语言所用的符号。
BMP 字符的 Unicode 编码表示为 U+hhhh,其中每个 h 代表一个十六进制数位。与 UCS-2 编码完全相同。对应的4字节 UCS-4 编码后两个字节一致,前两个字节的所有位均为0。
2.实现方式
Unicode 的实现方式不同于编码方式。一个字符的 Unicode 编码是确定的。但是在实际传输过程中,由于不同系统平台的设计不一定一致,以及出于节省空间的目的,对 Unicode 编码的实现方式有所不同。Unicode 的实现方式称为Unicode转换格式(Unicode Translation Format,简称为 UTF)。
例如,如果一个仅包含基本7位ASCII字符的 Unicode 文件,如果每个字符都使用2字节的原 Unicode 编码传输,其第一字节的8位始终为0。这就造成了比较大的浪费。对于这种情况,可以使用 UTF-8 编码,这是一种变长编码,它将基本7位ASCII字符仍用7位编码表示,占用一个字节(首位补0)。而遇到与其他 Unicode 字符混合的情况,将按一定算法转换,每个字符使用1-3个字节编码,并利用首位为0或1进行识别。这样对以7位ASCII字符为主的西文文档就大大节省了编码长度(具体方案参见UTF-8)。类似的,对未来会出现的需要4个字节的辅助平面字符和其他 UCS-4 扩充字符,2字节编码的 UTF-16 也需要通过一定的算法进行转换。
再如,如果直接使用与 Unicode 编码一致(仅限于 BMP 字符)的 UTF-16 编码,由于每个址 加昧肆礁鲎纸冢 赣acintosh机和PC机上对字节顺序的理解是不一致的。这时同一字节流可能会被解释为不同内容,如编码为 U+594E 的字符“奎”同编码为 U+4E59 的“乙”就可能发生混淆。于是在 UTF-16 编码实现方式中使用了大尾序(big-endian)、小尾序(little-endian)的概念,以及BOM(Byte Order Mark)解决方案。(具体方案参见UTF-16)
此外 Unicode 的实现方式还包括 UTF-7、Punycode、CESU-8、SCSU、UTF-32等,这些实现方式有些仅在一定的国家和地区使用,有些则属于未来的规划方式。目前通用的实现方式是 UTF-16小尾序(BOM)、UTF-16大尾序(BOM)和 UTF-8。在微软公司Windows XP操作系统附带的记事本中,“另存为”对话框可以选择的四种编码方式除去非 Unicode 编码的 ANSI 外,其余三种“Unicode”、“Unicode big endian”和“UTF-8”即分别对应这三种实现方式。
目前辅助平面的工作主要集中在第二和第三平面的中日韩统一表意文字中,因此包括GBK、GB18030、Big5等简体中文、正体中文、日文、韩语以及越南字喃的各种编码与 Unicode 的协调性被重点关注。考虑到 Unicode 最终要涵盖所有的字符,从某种意义而言,这些编码方式也可视作 Unicode 的出现于其之前的既成事实的实现方式,如同ASCII及其扩展Latin-1一样,后两者的字符在16位 Unicode 编码空间中的编码第一字节各位全为0,第二字节编码与原编码完全一致。但上述东亚语言编码与 Unicode 编码的对应关系要复杂得多。
非 Unicode 环境
在非 Unicode 环境下,由于不同国家和地区采用的字符集不一致,很可能出现无法正常显示所有字符的情况。微软公司使用了代码页(Codepage)转换表的技术来过渡性的部分解决这一问题,即通过指定的转换表将非 Unicode 的字符编码转换为同一字符对应的系统内部使用的 Unicode 编码。可以在“语言与区域设置”中选择一个代码页作为非 Unicode 编码所采用的默认编码方式,如936为简体中文GBK,950为正体中文Big5(皆指PC上使用的)。在这种情况下,一些非英语的欧洲语言编写的软件和文档很可能出现乱码。而将代码页设置为相应语言中文处理又会出现问题,这一情况无法避免。从根本上说,完全采用统一编码才是解决之道,但目前上无法做到这一点。
代码页技术现在广泛为各种平台所采用。UTF-7 的代码页是65000,UTF-8 的代码页是65001。
Unicode 目前已经有5.0版本。世界上有一大批计算机、语言学等科学家专门研究Unicode,到了现在Unicode标准已经不单是一个编码标准,还是记录人类语言文字资料的一个巨大的数据库,同时从事人类文化遗产的发掘和保护工作。
对于中文而言,Unicode 16编码里面已经包含了GB18030里面的所有汉字(27484个字),目前Unicode标准准备把康熙字典的所有汉字放入到Unicode 32bit编码中。
简单地说,Unicode扩展自ASCII字元集。在严格的ASCII中,每个字元用7位元表示,或者电脑上普遍使用的每字元有8位元宽;而Unicode使用全16位元字元集。这使得Unicode能够表示世界上所有的书写语言中可能用於电脑通讯的字元、象形文字和其他符号。Unicode最初打算作为ASCII的补充,可能的话,最终将代替它。考虑到ASCII是电脑中最具支配地位的标准,所以这的确是一个很高的目标。
Unicode影响到了电脑工业的每个部分,但也许会对作业系统和程式设计语言的影响最大。从这方面来看,我们已经上路了。Windows NT从底层支援Unicode(不幸的是,Windows 98只是小部分支援Unicode)。先天即被ANSI束缚的C程式设计语言通过对宽字元集的支援来支援Unicode。
字符串基础 ASCII MBCS UNICODE DBCS
有的字符串类都起源于C语言的字符串,而C语言字符串则是字符的数组。首先了解一下字符类型。有三种编码方式和三种字符类型。
第一种编码方式是单字节字符集,称之为SBCS,它的所有字符都只有一个字节的长度。ASCII码就是SBCS。SBCS字符串由一个零字节结尾。
第二种编码方式是多字节字符集,称之为MBCS,它包含的字符中有单字节长的字符,也有多字节长的字符。Windows用到的MBCS只有二种字符类型,单字节字符和双字节字符。
因此Windows中用得最多的字符是双字节字符集,即DBCS,通常用它来代替MBCS。
在DBCS编码中,用一些保留值来指明该字符属于双字节字符。例如,Shift-JIS(通用日语)编码中,值0x81-0x9F 和 0xE0-0xFC 的意思是:
“这是一个双字节字符,下一个字节是这个字符的一部分”。这样的值通常称为前导字节(lead byte),总是大于0x7F。
前导字节后面是跟随字节(trail byte)。DBCS的跟随字节可以是任何非零值。与SBCS一样,DBCS字符串也由一个零字节结尾。
第三种编码方式是Unicode。 Unicode编码标准中的所有字符都是双字节长。
有时也将Unicode称为宽字符集(wide characters),因为它的字符比单字节字符更宽(使用更多内存)。
注意,Unicode不是MBCS - 区别在于MBCS编码中的字符长度是不同的。Unicode字符串用二个零字节字符结尾(一个宽字符的零值编码)。
单字节字符集是拉丁字母,重音文字,用ASCII标准定义,用于DOS操作系统。双字节字符集用于东亚和中东语言。Unicode用于COM和Windows NT内部。
读者都很熟悉单字节字符集,它的数据类型是char。双字节字符集也使用char数据类型(双字节字符集中的许多古怪处之一)。
Unicode字符集用wchar_t数据类型。Unicode字符串用L前缀起头,如:
wchar_t wch = L'1'; // 2 个字节, 0x0031 wchar_t* wsz = L"Hello"; // 12 个字节, 6 个宽字符