`

Unicode 的介绍

 
阅读更多

Unicode 的介绍

1.双字节字符集

Windows支持四个不同的双字节字符集:代码页932(日文)、936(简体中文)、949(韩语)和950(繁体汉字)。

Unicode是统一的16位系统,这样就允许表示65,536个字符。这对表示所有字符及世界上使用象形文字的语言,包括一系列的数学、符号和货币单位符号的集合来说是充裕的。

「宽字符集」。「Unicode中的每个字符都是16位宽而不是8位宽。」

前128个Unicode字符(16位代码从0x0000到0x007F)就是ASCII字符,而接下来的128个Unicode字符(代码从0x0080到0x00FF)是ISO 8859-1对ASCII的扩展。

Unicode的最大好处是这里只有一个字符集,没有一点含糊。 Unicode字符串占用的内存是ASCII字符串的两倍。

2.宽字符和C

ANSI C也支持多字节字符集,例如中文、日文和韩文版本Windows支持的字符集。这些多字节字符集被当成单字节构成的字符串看待
宽字符不需要是Unicode。Unicode是一种可能的宽字符集。

3.Char 数据形态

char c = 'A' ; 该字母A是ASCII代码 同时是一个字节表示。

char * P = "Hello!"; 其中指针P 表示占有4个字节,即初始化一个指向字符串指针。

char a[10] 表示数组保留了 10个字符储存空间。 用表达式Sizeof(a)可返回10.

char a[] = "Hello" 该数组定义为一个函数的区域变量,则必须将它定义为一个 static 变量。

4.宽字符

理论上,C中1个字节可比8位长,打但习惯上 1个字节(1个char)是8位宽。

在C中宽字符是基于wchar_t数据形态,他们表头文件包含WCHAR.H中定义的。因此说是无符号短整数型 16位宽。

例如下面语句:

wchar_t c = 'A'; 其中变量c是双字节值,而Unicode表示字母A

wchar_t * p = L"Hello"; 其中大些字母L(代表「long」)。该字符串按宽字符保存-即每个字符占用2个字节。
通常,指针变量p要占用4个字节,而字符串变量需要14个字节-每个字符需要2个字节,末尾的0还需要2个字节。
(即 Hello 6个 *2 = 12字节在加上末尾2个字节)

定义宽字符数组:
static wchar_t a[] = L"Hello!" ;
该字符串也需要14个字节的储存空间,只有带有L,编译器才知道您需要将字符串存为每个字符2字节。
您还可在单个字符文字前面使用L前缀,来表示它们应解释为宽字符。如下所示:
wchar_t c = L'A' ;

5.宽字符链接库函数

在 ANSCII 中 char * pc = "Hello!"; 可用 iLength = strlen (pc) ;

在Unicode(宽字符)中 wchar_t * pw = L"Hello!" ; 应该用 iLength = strlen (pc) ;
strlen函数的宽字符版是wcslen(wide-character string length:宽字符串长度),

STRING.H(其中也说明了strlen)和WCHAR.H中均有说明。strlen函数说明如下:
size_t __cdecl strlen (const char *) ;

而wcslen函数则说明如下:
size_t __cdecl wcslen (const wchar_t *) ;

要得到宽字符串的长度可以呼叫

iLength = wcslen (pw) ; 
函数将返回字符串中的字符数6。

维护单一原始码

Unicode,程序中的每个字符串都将占用两倍的储存空间。您将发现宽字符执行时期链接库中的函数比常规的函数大。
最好的解决办法是维护既能按ASCII编译又能按Unicode编译的单一原始码文件。这将在处理前面有L的字符串文字时遇到麻烦。

使用Microsoft Visual C++包含的TCHAR.H表头文件。该表头文件不是ANSI C标准的一部分,因此那里定义的每个函数和宏定义的前面都有一条底线。
TCHAR.H为需要字符串参数的标准执行时期链接库函数提供了一系列的替代名称(例如,_tprintf和_tcslen)。
因为它们既可以指向函数的Unicode版也可以指向非Unicode版。

如果定义了名为_UNICODE的标识符,并且程序中包含了TCHAR.H表头文件,
那么_tcslen就定义为wcslen:(宽字符)
#define _tcslen wcslen

如果没有定义UNICODE,则_tcslen定义为strlen:(ASCii)
#define _tcslen strlen
等等。

TCHAR.H还用一个新的数据型态TCHAR来解决两种字符数据型态的问题。

如果定义了_UNICODE标识符,那么TCHAR就是wchar_t: (宽字符)
typedef wchar_t TCHAR ;
否则,TCHAR就是Char:
typedef char TCHAR ; (ASCii)

现在开始讨论字符串文字中的L问题。如果定义了_UNICODE标识符,那么一个称作__T的宏就定义如下:
#define __T(x) L##x

这是相当晦涩的语法,但合乎ANSI C标准的前置处理器规范。那一对井字号称为「粘贴符号(token paste)」,
将字母L添加到宏参数上。因此,如果宏参数是"Hello!",则L##x就是L"Hello!"。

如果没有定义_UNICODE标识符,则__T宏只简单地定义如下:
#define __T(x) x
此外,还有两个宏与__T定义相同:

#define _T(x)__T(x)
#define _TEXT(x)__T(x)
在Win32 console程序中使用哪个宏,取决于您喜欢简洁还是详细。基本地,必须按下述方法在_T或_TEXT宏内定义字符串文字:

_TEXT ("Hello!")
这样做的话,如果定义了_UNICODE,那么该串将解释为宽字符的组合,否则解释为8位的字符字符串。


Windows表头文件类型

Windows.H 头文件(包含)—>WINDEF.H(包含)—>WINNT.H(处理Unicode)(包含)—>CTYPE.H(头文件支持 Wchar_t)

typedef char CHAR ;
typedef wchar_t WCHAR ; // wc 匈牙利标记 -- 注释 WC

当您需要定义8位字符或者16位字符时,推荐您在Windows程序中使用的数据型态是CHAR和WCHAR。WCHAR定义后面的注释是匈牙利标记法的建议:一个基于WCHAR数据型态的变量可在前面附加上字母wc以说明一个宽字符。

WINNT.H表头文件进而定义了可用做8位字符串指针的六种数据型态和四个可用做const 8位字符串指针的数据型态。(支持 windows 16位)这里精选了表头文件中一些实用的说明数据型态语句:

支持windows 16 位
typedef CHAR * PCHAR, * LPCH, * PCH, * NPSTR, * LPSTR, * PSTR ;
typedef CONST CHAR * LPCCH, * PCCH, * LPCSTR, * PCSTR ;
前缀N和L表示「near」和「long」,指的是16位Windows中两种大小不同的指标。在Win32中near和long指标没有区别。

类似地,WINNT.H定义了六种可作为16位字符串指针的数据型态和四种可作为const 16位字符串指针的数据型态:

支持windows 32 位
TCHAR.H 和 WINNT.H 同样作用
typedef WCHAR * PWCHAR, * LPWCH, * PWCH, * NWPSTR, * LPWSTR, * PWSTR ;
typedef CONST WCHAR * LPCWCH, * PCWCH, * LPCWSTR, * PCWSTR ;

至此,我们有了数据型态CHAR(一个8位的char)和WCHAR(一个16位的wchar_t),以及指向CHAR和WCHAR的指标。与TCHAR.H一样,WINNT.H将TCHAR定义为一般的字符类型。如果定义了标识符UNICODE(没有底线),则TCHAR和指向TCHAR的指标就分别定义为WCHAR和指向WCHAR的指标;如果没有定义标识符UNICODE,则TCHAR和指向TCHAR的指标就分别定义为char和指向char的指标:

#ifdef UNICODE // 宽字符串
typedef WCHAR TCHAR, * PTCHAR ;
typedef LPWSTR LPTCH, PTCH, PTSTR, LPTSTR ;
typedef LPCWSTR LPCTSTR ;
#else // 非宽字符串
typedef char TCHAR, * PTCHAR ;
typedef LPSTR LPTCH, PTCH, PTSTR, LPTSTR ;
typedef LPCSTR LPCTSTR ;
#endif

如果已经在某个表头文件或者其它表头文件中定义了TCHAR数据型态,那么WINNT.H和WCHAR.H表头文件都能防止其重复定义。不过,无论何时在程序中使用其它表头文件时,都应在所有其它表头文件之前包含WINDOWS.H。

WINNT.H表头文件还定义了一个宏,该宏将L添加到字符串的第一个引号前。如果定义了UNICODE标识符则一个称作 __TEXT的宏定义如下

#define __TEXT(quote) L##quote --》 加L为宽字符串
如果没有定义标识符UNICODE,则像这样定义__TEXT宏:

#define __TEXT(quote) quote --》 为加不是宽字符串
此外, TEXT宏可这样定义:

#define TEXT(quote) __TEXT(quote)
这与TCHAR.H中定义_TEXT宏的方法一样,只是不必操心底线。我将在本书中使用这个宏的TEXT版本。

这些定义可使您在同一程序中混合使用ASCII和Unicode字符串,或者编写一个可被ASCII或Unicode编译的程序。如果您希望明确定义8位字符变量和字符串,请使用CHAR、PCHAR(或者其它),以及带引号的字符串。为明确地使用16位字符变量和字符串,请使用WCHAR、PWCHAR,并将L添加到引号前面。对于是8位还是16位取决于UNICODE标识符的定义的变量或字符串,要使用TCHAR、PTCHAR和TEXT宏
8位 - CHAR.PCHAR,带引号字符串
ASCII和Unicode
16位 - WCHAR.PWCHAR,将L添加到引号前。

Windows函数呼叫

(Win32位 兼含16位USER.EXE,还有USER32.DLL的动态链接库32位)

从Windows 1.0到Windows 3.1的16位Windows中,MessageBox函数位于动态链接库USER.EXE。在Windows 3.1软件开发套件的WINDOWS.H中,MessageBox函数定义如下:

int WINAPI MessageBox (HWND, LPCSTR, LPCSTR, UINT) ;

32位 MessageBox 函数进入点
MessageBoxA (ASCII)
MessageBoxW (UNicode)
注意,函数的第二个、第三个参数是指向常数字符串的指针。当编译连结一个Win16程序时,Windows并不处理MessageBox呼叫。程序.EXE文件中的表格,允许Windows将该程序的呼叫与USER中的MessageBox函数动态链接起来。

32位的Windows(即所有版本的Windows NT,以及Windows 95和Windows 98)除了含有与16位兼容的USER.EXE以外,还含有一个称为USER32.DLL的动态链接库,该动态链接库含有32位使用者接口函数的进入点,包括32位的MessageBox。

这就是Windows支持Unicode的关键:在USER32.DLL中,没有32位MessageBox函数的进入点。实际上,有两个进入点,一个名为MessageBoxA(ASCII版),另一个名为MessageBoxW(宽字符版)。用字符串作参数的每个Win32函数都在操作系统中有两个进入点!幸运的是,您通常不必关心这个问题,程序中只需使用MessageBox。与TCHAR表头文件一样,每个Windows表头文件都有我们需要的技巧

下面是MessageBoxA在WINUSER.H中定义的方法。这与MessageBox早期的定义很相似:

WINUSERAPI int WINAPI MessageBoxA (HWND hWnd, LPCSTR lpText, // 单字符串函数
LPCSTR lpCaption, UINT uType) ;

下面是MessageBoxW:

WINUSERAPI int WINAPI MessageBoxW (HWND hWnd, LPCWSTR lpText, // 宽字符串函数

LPCWSTR lpCaption, UINT uType) ;

注意,MessageBoxW函数的第二个和第三个参数是指向宽字符的指针。

如果需要同时使用并分别匹配ASCII和宽字符函数呼叫,那么您可在Windows程序中明确地使用MessageBoxA和MessageBoxW函数。但大多数程序写作者将继续使用MessageBox。根据是否定义了UNICODE,MessageBox将与MessageBoxA或MessageBoxW一样。在WINUSER.H中完成这一技巧时,程序相当琐碎:

#ifdef UNICODE

#define MessageBox MessageBoxW

#else

#define MessageBox MessageBoxA

#endif
宽字符串
这样,如果定义了UNICODE标识符,那么程序中所有的MessageBox函数呼叫实际上就是MessageBoxW函数;否则,就是MessageBoxA函数。

执行该程序时,Windows将程序中不同的函数呼叫与不同的Windows动态链接库的进入点连结。虽然只有少数例外,但是,在Windows 98中不能执行Unicode版的Windows函数。虽然这些函数有进入点,但通常返回错误代码。应用程序注意这些返回的错误并采取一些合理的动作。

Windows的字符串函数


正如前面谈到的,Microsoft C包括宽字符和需要字符串参数的C语言执行时期链接库函数的所有普通版本。不过,Windows复制了其中一部分。例如,下面是Windows定义的一组字符串函数,这些函数用来计算字符串长度、复制字符串、连接字符串和比较字符串:

ILength = lstrlen (pString) ; // 计算字符串长度

pString = lstrcpy (pString1, pString2) ; // 复制字符串

pString = lstrcpyn (pString1, pString2, iCount) ; // 连接字符串

pString = lstrcat (pString1, pString2) ; // 以下都为比较字符串

iComp = lstrcmp (pString1, pString2) ;

iComp = lstrcmpi (pString1, pString2) ;

这些函数与C链接库中对应的函数功能相同。如果定义了UNICODE标识符,那么这些函数将接受宽字符串,否则只接受常规字符串。宽字符串版的lstrlenW函数可在Windows 98中执行。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics