字符编码和字符集

本文总结主要来自

《C++标准库 第2版》,Nicolai M. Josuttis著,侯捷译。

《Windows程序设计(第5版 珍藏版)》,Charles Petzold著,方敏 张胜 梁路平 赵勇 等译。

    在计算机科学刚萌芽的年代,计算机的字符集局限于英文字母系统。今天,在全球化领域中,有些字符集使用高达32bit,可表达一百万以上的字符量。这也导致在不同的国家和文化中以不同的标准和方法来处理字符。

1 多字节(Multibyte)宽字符(Wide-Character)文本

    对于多余256个字符的字符集,有两种不同的解决方案:多字节和宽字符表示法。

    在多字节表示法中,字符所用的byte个数是变动的。一个1-byte字符(如ISO Latin-1字符)后面可跟一个3-byte字符。

    宽字符表示法,字符所用的byte数目恒定,与所表示的字符无关。典型的byte个数是2或4。概念上讲,这和那些只使用1byte的表示法没什么区别。

2 不同的字符集

  • US-ASCII,这是个7-bit字符集,这个字符集是其他所有字符集的基础,往往各字符集内介于0x20和0x7F之间有着与此相同的字符。
  • ISO-Latin-1,这是个8-bit字符集,这个字符集也作为其他所有字符集的基础,往往各字符集内介于0x20至0x7F和0xA0至0xFF之间有着与此相同的字符。
  • UCS-2,这是个16-bit定长字符集,提供Universal Character Set(全球字符集)和Unicode(统一码)中最重要的65536个字符。
  • UTF-8,这是个multibyte字符集,使用1-4个8bit值,用来表现Universal Character Set和Unicode的所有字符。它被广泛使用于万维网(World Wide Web)。
  • UTF-16,这是个multibyte字符集,使用1-2个code unit(每个16bit),用来表现Universal Character Set和Unicode的所有字符。
  • UCS-4或UTF-32,这是个32-bit定长字符集,提供Universal Character Set和Unicode之所有标准化字符。

    注意,UTF-16和UTF-32可能会在其完整字符序列一开始放个byte order mark(BOM),用以标示它所使用的是big-endian(此为默认)或little-endian。

3 在C++中处理字符集

    C++提供不同字符类型用以应付前述字符集:

  • char可被用于所有8bit以下的字符集,US-ASCII,ISO-Latin-1和ISO-Latin-9。此外它可被用于UTF-8的8bit值。
  • char16_t(始自C++11)可被用于UCS-2,也可用于UTF-16的code unit。
  • char32_t(始自C++11)可被用于UCS-4/UTF-32。
  • wchar_t是所有被支持之locale中的最大扩充字符集内的类型。它通常等价于char16_t或者char32_t。

    注意,自C++11起,你可以在指明string literal(字符串字面值常量)时使用不同的字符编码。为了支持字符和编码的转换,C++标准库提供了以下性质:

  • 欲将string转换为wstring,或反向转换,可使用ctype<>facet的成员函数widen()和narrow()。它们能够被用来转换固有(native)字符集中的字符成为某地域(locale)字符集中的字符,只要两者都使用字符类型char。
  • 欲将一连串multibyte转换为wstring,或反向转换,可使用class template wstring_convert<>以及相应的codecvt<>facet。
  • Class codecvt<>可被class basic_filebuf<>用来在内部和外部表述之间进行转换——当读文件或写文件时。
  • 欲读或写一连串multibyte字符,可使用class wbuffer_convert<>以及相应的codecvt<>facet。

4 Unicode简介

    简单来说,Unicode是ASCII字符编码的一个扩展,Unicode用的是16位字符编码,而不是像ASCII那样7位代表一个字符,也不是计算机上常见的每个字符8位方式。这样,在计算机通信中,世界上所有书面语言中的字母、象形文字和其他符号都可以用Unicode来表示。

4.1 双字节字符集

    双字节字符集(DBCS),前128个代码是ASCII,但是,较高的128个代码中有些还跟随者第二个字节。这两个字节在一起代表一个单独的字符,常常是一个复杂的象形文字。双字节字符集的问题不在于字符是由两个字节组成的,问题是有些字符是由一个字节组成的,这就导致了奇怪的编程问题,如一个字符创的字符长度不能因字节数量而决定。字符串的长度要解析后才能判断,每个字节都要检查是不是双字节的前导字节。

4.2 Unicode解救方案

    Unicode被认为是(特别是在C编程语言环境中)“宽字符”。每一个在Unicode里的字符是16位宽而不是8位宽。与此相反,在双字节字符集里,某些单字节本身就定义了一个字符。Unicode最棒的地方是,它只有一个字符集。避免了二义性。宽字符并不一定是Unicode。Unicode只是宽字符编码的一种实现。

    使用Unicode或者更宽字符并不会改变C语言中的字符数据类型,char类型继续代表1个字节的存储空间,而且sizeof(char)继续返回1。C语言中宽字符是基于wchar_t数据类型的。这个数据类型被定义在多个头文件中,包括WCHAR.H,如下:

typedef unsigned short wchar_t;

    因此,wchar_t数据类型和一个无符号短整型一样,是16位宽。可以用下面的语句来定义一个包含单个宽字符的变量:

wchar_t c='A';变量c是一个两个字节的值0x0041,这是个Unicode中字母A的代表。(然而,因为Intel微软处理器存储多字节数值时总是最低有效字节优先,所以这些字节实际上在内存中存储顺序是:0x41,0x00.如果要检查Unicode文本的内存存储,务必记住这点。)

    还可以定义一个已经初始化的指向宽字符串的指针:wchar_t *p=L"Hello";大写L表示长整型。这向编译器表明这个字符串使用宽字符存储(引号之前的那个L非常重要,这两个符号之间绝对不能有空格,只有有了这个L,编译器才知道字符创的每个字符用两个字节存储)。指针p还是需要4个字节的存储空间。

    C语言自身和运行库函数有差别,编译器将字符串解释为一个16位短整型的集合并把它们存储在wchar_t数组,编译器还会处理所有数组索引和sizeof操作符,因此这些都会正常工作。但是,程序运行时,运行库函数(如strlen)是在链接时被增加进去的,这些函数期望收到由单字节字符构成的字符串,因此在遇到宽字符时,它们不会像我们预期那样执行。宽字符版本的strlen函数被称为wcslen。

    如果希望创建两个版本的程序,一个用ASCII字符串而另一个用Unicode字符串。最好的办法是维护一个单一的源代码文件,但可以变异成ASCII或Unicode(两者运行库函数具有不同的名称,字符变量的定义也不同,还有讨厌的在字符串字面之前必须要加L)。可以使用包含在Microsoft Visual C++中的TCHAR.H头文件。这个头文件并不是ANSI C标准的一部分,所以其中定义的每一个函数和宏都有一个下划线前缀。TCHAR.H为那些需要字符创参数的普通运行库函数(如_tprintf和_tcslen)提供了一系列替代名称。这些函数有时被称为“通用”的函数名字,因为它们可以指Unicode或非Unicode版本的函数。如果一个命名为_UNICODE的标识符被定义了并且TCHAR.H头文件被包含在程序中,_tcslen就被定义为wcslen,如果标识符没有被定义,就被定义为strlen。

4.3 宽字符和Windows

    Windows程序包含着WINDOWS.H头文件。该文件又包含着许多其他头文件,例如WINDEF.H,该文件中有许多在Windows中使用的基本数据类型的定义,同时它本身包含WINNT.H。WINNT.H负责处理基本的Unicode支持的功能。

    WINNT.H在一开始就包含C的头文件CTYPE.H,这是C的众多头文件之一,包含着wchar_t的定义。WINNT.H定义了两个新的被称作CHAR和WCHAR的数据类型。它们是写Windows程序时推荐使用的数据类型,分别用于定义8位或者16位的字符。WINNT.H还定义了可用作8位和16位字符串指针(如PCHAR、PWCHAR)。像在TCHAR.H中一样,WINNT.H将TCHAR定义为一个通用的字符类型。如果标识符UNICODE(没有下划线)被定义了,则TCHAR被定义为WCHAR;否则指向CHAR。如果TCHAR已经在某个头文件中被定义了,WINNT.H和WCHAR.H头文件都能防止TCHAR数据类型被重复定义。不管怎样,无论何时程序中使用其他头文件时,都应该在所有其他头文件之前先包含WINDOWS.H头文件。

    WINNT.H头文件还定义了一个宏,_TEXT,它将L添加到一个字符串的第一个引号前。TEXT宏有同样的作用,只是不需要考虑下划线。

    Windows支持Unicode的关键是:在USER32.DLL中,并没有32位MessageBox函数的入口点。实际上,它有两个入口点,一个名为MessageBoxA(ASCII版),另一个名为MessageBoxW(宽字符版)。像这样的用字符串作参数的每个Win32函数,都在操作系统中有两个入口点。如果程序中混合调用并比配ASCII和宽字函数,则可以明确使用MessageBoxA和MessageBoxW函数。但大多数情况根据是否定义UNICODE标识符,自动调用MessageBox即可。

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Unicode和UTF之间区别:

简单来说:Unicode是【字符集】;UTF是【编码规则】

广义的Unicode是一个标准,定义了一个字符集和一系列编码规则,即Unicode字符集和UTF-8、UTF-16、UTF-32编码规则。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值