Chinaunix首页 | 论坛 | 博客
  • 博客访问: 75686
  • 博文数量: 35
  • 博客积分: 1640
  • 博客等级: 上尉
  • 技术积分: 400
  • 用 户 组: 普通用户
  • 注册时间: 2008-05-05 11:44
文章分类

全部博文(35)

文章存档

2011年(1)

2008年(34)

我的朋友
最近访客

分类: C/C++

2008-05-05 12:51:40

Unicode

    Unicode是统一的16位系统,这样就允许表示65536个字符。这就足够表示所有字符。

Unicode实用“宽字符集”。Unicode中的每个字符都是16位宽而不是8位宽。在Unicode中,没有单单使用8位数值得意义存在。(注意,双字节字符集不同于Unicode

Unicode的前128个字符(16位代码从0x00000x007F)就是ASCII字符,接下来的是各种扩展的字符。

宽字符

    多个字节代表一个字符的字符集。

    多字节字符集主要影响C语言程序执行时期链接库函数。而且可能会引起一些编译问题。

    Unicode是宽字符集的一种。

Char数据类型

    定义并初始化了一个只包含一个字符的变量:

char          c = “A”;  

    变量c需要1个字节来保存,并将用十六进制数0x41初始化,这是字母AASCII码。

    可以像这样定义一个指向字符串的指针:

         char    *p;

    因为windows是一个32位操作系统,所以指针变量p需要用4个字节保存。

    还可初始化一个指向字符串的指针:

         char    *p = “Hello!”;

    变量p也需要用4个字节保存。该字符串保存在静态内存中并占用7个字节。

    还可以像这样定义字符数组:

         char          a[10];

    该数组占据10字节的空间。

宽字符

    Unicode或者宽字符都没有改变char数据类型在C中的含义。Char继续表示1个字节的存储空间,sizeof(char)继续返回1。理论上,C1个字节可以比8位长,但通常认为8位。

    C中的宽字符基于wchar_t数据类型,它在几个表头文件包括WCHAR.H中都有定义,如下:

typedef unsigned short wchar_t ;

    因此,wchar_t数据型态与无符号短整数型态相同,都是16位宽。

    要定义包含一个宽字符的变量,可使用下面的语句:

    wchar_t   c = 'A' ;

    变量c是一个双字节值0x0041,是Unicode表示的字母A。(在Intel微处理器中,该字节实际上是以0x410x00的顺序保存在内存中。)

    您还可定义指向宽字符串的指针:

wchar_t           * p = L"Hello!" ;

    指针变量p要占用4个字节,而字符串变量需要14个字节。(注意紧接在第一个引号前面的大写字母L(代表「long」)。这将告诉编译器该字符串按宽字符保存-即每个字符占用2个字节。)

    可以用下面的语句定义宽字符数组:

static wchar_t         a[] = L"Hello!" ;

    该字符串也需要14个字节的储存空间,sizeof (a) 将返回14。索引数组a可得到单独的字符。a[1] 的值是宽字符「e」,或者0x0065

    虽然看上去更像一个印刷符号,但第一个引号前面的L非常重要,并且在两个符号之间必须没有空格。只有带有L,编译器才知道您需要将字符串存为每个字符2字节。

宽字符链接库函数

例如,定义一个字符串指针:

char * pc = "Hello!" ;

调用strlen得到字符串的长度(strlen( )是一个库函数):

iLength = strlen (pc) ;

结果:iLength 6

现在定义一个指向宽字符的指针:

wchar_t * pw = L"Hello!" ;

同样调用strlen

iLength = strlen (pw) ;

现在麻烦来了。首先,C编译器会显示一条警告消息,可能是这样的内容:

'function' : incompatible types - from 'unsigned short *' to 'const char *'

这条消息的意思是:声明strlen函数时,该函数应接收char类型的指标,但它现在却接收了一个unsigned short类型的指标。您仍然可编译并执行该程序,但您会发现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语言本身和执行时期链接库函数之间的区别。编译器将字符串L"Hello!" 解释为一组16位短整数型态数据,并将其保存在wchar_t数组中。编译器还处理数组索引和sizeof操作符,因此这些都能正常工作,但在连结时才添加执行时期链接库函数,例如strlen。这些函数认为字符串由单字节字符组成。遇到宽字符串时,函数就不像我们所希望那样执行了。

为了处理宽字符,重写了一些字符串处理函数:

strlen函数的宽字符版是wcslenwide-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。请记住,改成宽字节后,字符串的字符长度不改变,只是位组长度改变了。

您熟悉的所有带有字符串参数的C执行时期链接库函数都有宽字符版。例如,wprintfprintf的宽字符版。这些函数在WCHAR.H和含有标准函数说明的表头文件中说明。

为了兼容Unicode和单字节字符,在TCHAR.H表头文件中定义了一系列的宏定义,以方便程序编写。

例如,有这样的定义:

#define     __T(x)       L##x  //##(粘贴符号),将字符L添加到宏参数上。

#define     _TEXT(X)           __T(x)

在程序中,如果使用了_TEXT(“Hello!”),如果定义了_UNICODE,那么该串解释为宽字符的组合(L”Hello!”),否则解释为8位字符的字符串。

Windows表头文件类型

一个Windows程序包括表头文件WINDOWS.H。该文件包括许多其它表头文件,包括WINDEF.H,该文件中有许多在Windows中使用的基本型态定义,而且它本身也包括WINNT.HWINNT.H处理基本的Unicode支持。并且,无论何时在程序中使用其它表头文件时,都应在所有其它表头文件之前包含WINDOWS.H

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

#define TEXT(quote) __TEXT(quote)

Windows函数调用

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

这就是Windows支持Unicode的关键:在USER32.DLL中,没有32MessageBox函数的进入点。实际上,有两个进入点,一个名为MessageBoxAASCII版),另一个名为MessageBoxW(宽字符版)。用字符串作参数的每个Win32函数都在操作系统中有两个进入点!

执行该程序时,Windows将程序中不同的函数呼叫与不同的Windows动态链接库的进入点连结。这样,如果定义了UNICODE标识符,那么程序中所有的MessageBox函数呼叫实际上就是MessageBoxW函数;否则,就是MessageBoxA函数。如果需要同时使用并分别匹配ASCII和宽字符函数呼叫,那么您可在Windows程序中明确地使用MessageBoxAMessageBoxW函数。

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标识符,那么这些函数将接受宽字符串,否则只接受常规字符串。

Windows中使用printf

随着宽字符的发表,sprintf类型的函数增加许多,使得函数名称变得极为混乱。下表列出了MicrosoftC执行时期链接库和Windows支持的所有sprintf函数。

 

  ASCII 宽字符 常规
参数的变数个数
标准版 sprintf swprintf _stprintf
最大长度版 _snprintf _snwprintf _sntprintf
Windows wsprintfA wsprintfW wsprintf
参数数组的指针    
标准版 vsprintf vswprintf _vstprintf
最大长度版 _vsnprintf _vsnwprintf _vsntprintf
Windows wvsprintfA wvsprintfW wvsprintf
 

在宽字符版的sprintf函数中,将字符串缓冲区定义为宽字符串。在宽字符版的所有这些函数中,格式字符串必须是宽字符串。不过,您必须确保传递给这些函数的其它字符串也必须由宽字符组成。

示例:

展示了如何实作MessageBoxPrintf函数

/*******************************************************************

SCRNSIZE.C: Displays screen size in a message box

*******************************************************************/

 

#include

#include

#include

int CDECL MessageBoxPrintf(TCHAR *szCaption, TCHAR *szFormat, ...)

{

     TCHAR    szBuffer[1024];

     va_list  pArgList;

     va_start(pArgList, szFormat);

    _vsntprintf(szBuffer, sizeof(szBuffer)/sizeof(TCHAR), szFormat, pArgList);

     va_end(pArgList);

     return MessageBox(NULL, szBuffer, szCaption, 0);

}

int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hPrevinstance, PSTR szCmdLine, int iCmdShow)

{

     int cxScreen, cyScreen;

     cxScreen = GetSystemMetrics(SM_CXSCREEN);

     cyScreen = GetSystemMetrics(SM_CYSCREEN);

     MessageBoxPrintf(TEXT("ScmSize"), TEXT("The screen is %i pixels wide by %i pixels high."), cxScreen, cyScreen);

     return 0;

}

以上来自于《windows程序设计》第二章“Unicode简介”。

阅读(934) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~