《Windows程序设计》第二章 Unicode简介
字符集简史
先天即被ANSI 束缚的C程式设计语言通过对宽字元集的支援来支援Unicode。
开发ASCII 的过程中,在字元长度是6 位元、7 位元还是8 位元的问题上产生了很大的争议。从可靠性
的观点来看不应使用替换字元,因此ASCII 不能是6 位元编码,但由於费用的原因也排除了8 位元版本的方案(当时每位元的储存空间成本仍很昂贵)。这样,最终的字元码就有26 个小写字母、26 个大写字母、10 个数字、32 个符号、
33 个代号和一个空格,总共128 个字元码。
ASCII 有许多优点。例如,26 个字母代码是连续的(在EBCDIC 代码中就不
是这样的);大写字母和小写字母可通过改变一位元资料而相互转化;10 个数
位的代码可从数值本身方便地得到(在BCDIC代码中,字元「0」的编码在字元
「9」的後面!)
因为遵循了ANSI 草案和ISO 标准,纯
Windows字元集被称作「ANSI字元集」。 ANSI草案和ISO标准最终成为ANSI/ISO
8859-1-1987
,通常也简写为「Latin
1」。
MS-DOS
3.3(1987年4 月发行)向IBM
PC 用户引进了内码表(code
page)
的概念,Windows也使用此概念。
明白Unicode 和DBCS 之间的区别很重要。Unicode 使用(特别在C 程式设
计语言环境里)「宽字元集」。「Unicode 中的每个字元都是16 位元宽而不是
8 位元宽。」在Unicode 中,没有单单使用8 位元数值的意义存在。相比之下,
在双位元组字元集中我们仍然处理8 位元数值。
宽字符和C
Unicode或者宽字符都没有改变char数据型态在C中的含义。char继续表示1个字节的储存空间,sizeof (char)继续返回1。
C中的宽字符基于wchar_t数据型态,wchar_t数据型态与无符号短整数型态相同,都是16位宽。
wchar_t c = 'A';
wchar_t c = L'A'; // 通常这是不必要的,C编译器会对该字符进行扩充,使它成为宽字符
wchar_t * p = L"Hello!";
注意紧接在第一个引号前面的大写字母L(代表「long」)。这将告诉编译器该字符串按宽字符保存-即每个字符占用2个字节。通常,指针变量p要占用4个字节,而字符串变量需要14个字节-每个字符需要2个字节,末尾的0还需要2个字节。
宽字符和链接库函数
wchar_t * p = L"Hello!";
iLength = strlen(pw);
首先,C编译器会显示一条警告消息:incompatible types - from 'unsigned short *' to 'const char *'
仍然可编译并执行该程序,但您会发现iLength等于1。
字符串「Hello!」中的6个字符占用16位:
0x0048 0x0065 0x006C 0x006C 0x006F 0x0021
Intel处理器在内存中将其存为:
48 00 65 00 6C 00 6C 00 6F 00 21 00
假定strlen函数正试图得到一个字符串的长度,并把第1个字节作为字符开始计数,但接着假定如果下一个字节是0,则表示字符串结束。
事实上并不是每个C语言链接库函数都需要重写,只是那些有字符串参数的函数才需要重写,而且也不用由您来完成。它们已经重写完了。strlen函数的宽字符版是wcslen。
宽字符和Windows
Windows
NT从底层支援Unicode。这意味着Windows NT内部使用由16位字符组成的字符串。因为世界上其它许多地方还不使用16位字符串,所以Windows NT必须经常将字符串在操作系统内转换。Windows NT可执行为ASCII、Unicode或者ASCII和Unicode混合编写的程序。即,Windows NT支持不同的API函数呼叫,这些函数接受8位或16位的字符串。相对于Windows NT,Windows 98对Unicode的支持要少得多。只有很少的Windows
98函数呼叫支持宽字符串(包括MessageBox)。
一个Windows程序包括表头文件WINDOWS.H。该文件包括许多其它表头文件,包括WINDEF.H,该文件中有许多在Windows中使用的基本型态定义,而且它本身也包括WINNT.H。WINNT.H处理基本的Unicode支持。WINNT.H的前面包含C的表头文件CTYPE.H,这是C的众多表头文件之一,包括wchar_t的定义。WINNT.H定义了新的数据型态,称作CHAR和WCHAR。当您需要定义8位字符或者16位字符时,推荐您在Windows程序中使用的数据型态是CHAR和WCHAR。
typedef char CHAR;
typedef wchar_t WCHAR; // wc, 注释是匈牙利标记法的建议
前缀N和L表示「near」和「long」,指的是16位Windows中两种大小不同的指标。在Win32中near和long指标没有区别。WINNT.H表头文件进而定义了可用做8位字符串指针的六种数据型态和四个可用做const
8位字符串指针的数据型态。
typedef WCHAR * PWCHAR, * LPWCH, * PWCH, * NWPSTR, * LPWSTR, * PWSTR ;
typedef CONST WCHAR * LPCWCH, * PCWCH, * LPCWSTR, * PCWSTR ;
#ifdef UNICODE
typedef WCHAR TCHAR, * PTCHAR ;
#else
typedef char TCHAR, * PTCHAR ;
#endif
#define __TEXT(quote) L##quote
#define __TEXT(quote) quote
#define TEXT(quote) __TEXT(quote)
这些定义可使您在同一程序中混合使用ASCII和Unicode字符串,或者编写一个可被ASCII或Unicode编译的程序。如果您希望明确定义8位字符变量和字符串,请使用CHAR、PCHAR(或者其它),以及带引号的字符串。
为明确地使用16位字符变量和字符串,请使用WCHAR、PWCHAR,并将L添加到引号前面。
对于是8位还是16位取决于UNICODE标识符的定义的变量或字符串,要使用TCHAR、PTCHAR和TEXT宏。
Windows函数调用
从Windows 1.0到Windows 3.1的16位Windows中,MessageBox函数位于动态链接库USER.EXE。
int WINAPI MessageBox(HWND, LPCSTR, UINT);
实际上,有两个进入点,一个名为MessageBoxA(ASCII版),另一个名为MessageBoxW(宽字符版)。用字符串作参数的每个Win32函数都在操作系统中有两个进入点!
在Windows程序中不能使用printf,Windows对标准输入和标准输出没有概念。仍然可以使用sprintf及sprintf系列中的其它函数来显示文字。这些函数除了将内容格式化输出到函数第一个参数所提供的字符串缓冲区以外,其功能与printfI相同。然后便可对该字符串进行操作(例如将其传给MessageBox)。
char szBuffer[100];
sprintf(szBuffer, "The sum of %i and %i is %i", 5, 3, 5+3);
puts(szBuffer);