在标准C中,通过一种叫“宽字符”的概念来支持用多个字节代表一个字符的字符集。多字节字符集主要影响C语言运行库函数。宽字符并不一定是Unicode。Unicode只是宽字符编码的一种实现。我们进在Windows中,将宽字符和Unicode作为同义词。
更宽的字符
使用Unicode或者是宽字符并不会改变C语言中的字符数据类型。C语言中的宽字符是基于wchar_t数据类型的。这个数据类型被定义在多个头文件中。包括WCHAR.H,如下所示:
typedef unsigned short wchar_t;
可以用下面的语句来定义一个包含单个宽字符的变量:
wchar_t c = 'A';
也可以在单个字符串常量前面使用L前缀,如下所示,以表明它们应被解释为宽字符:
wchar_t c = L'A';
但这通常是不需要的,C编译器总是会在字符后面加0的。
还可以如下定义一个已初始化的指向宽字符串的指针:
wchar_t *p = L"Hello!";
宽字符的库函数
宽字符版本的strlen函数被称为wcslen,并定义在STRING.H和WCHAR.H中,strlen函数的声明如下:
size_t __cdecl strlen( const char *);
而wcslen函数的声明如下:
size_t __cdecl wcslen( const wchar_t *);
在使用宽字符的时候,字符串的字符长度并没有改变,改变的只是字节长度。
维护一个源代码文件
维护一个单一源代码文件,使之可以编译成ASCII或Unicde。其中一个方法是使用包含在VC中的TCHAR.H头文件。这个头文件并不是ANSI C标准的一部分,所以其中定义的每一个函数和宏都有一个下划线前缀。TCHAR.H为那些需要字符串参数的普通运行库函数(例如,_tprintf和_tcslen)提供了一系列的替代名称。这些函数有时被称为“通用”的函数名字,因为它们可以指定Unicode或非Unicode版本的函数。
如下定义,其中那一堆数字符号被称为“令牌粘贴”,它使得字母L和红参数拼接在一起。
#ifdef _UNICODE
typedef wchar_t TCHAR;
#define _tcslen wcslen
#define __T(x) L##x
#else
typedef char TCHAR;
#defin _tcslen strlen
#define __T(x) x
#endif
#define _T(x) __T(x)
#define _TEXT(x) __T(x)
Windows头文件的类型
Windows程序包含着WINDOWS.H头文件,该文件又包含着许多其他头文件,例如WINDEF.H,该文件中又有许多在Windows中使用的基本数据类型的定义,同时它本身也包含WINNT.H。WINNT.H则负责处理基本的Unicde支持功能。
WINNT.H在一开始就包含C的头文件CTYPE.H,而着是C的众多头文件之一,包含着wchar_t 的定义。WINNT.H定义了两个新的被称作CHAR和WCAHR的数据类型:
typedef char CHAR;
typedef wcahr_t WCHAR; //wc是匈牙利标记法,用来说明这是一个宽字符。
CHAR和WCHAR是写Windows程序时推荐使用的数据类型,它们分别用于定义8位或者16位的字符。WINNT.H将TCHAR定义为一个通用的字符类型。如果标识符UNICODE(没有下划线)被定义了,则TCHAR 和指向TCHAR的指针就被定义为WCHAR和指向WCHAR的指针;如果标识符UNICODE没有被定义,则TCHAR和指向TCHAR的指针就分别被定义为char和指向char的指针。WINNT.H头文件还定义了一个宏,它将L添加到一个字符串的第一个引号前。定义如下:
#ifdef UNICODE
typedef WCHAR TCHAR , * PTCHAR;
#define __TEXT(quote) L##quote
#else
typedef char TCHAR , * PTCHAR;
#define __TEXT(quote) quote
#endif
#define TEXT(quote) __TEXT(quote)
Windows函数调用
如果需要在程序中混合调用并匹配ASCII和宽字符函数,则可以明确使用MessageBoxA和MessageBoxW函数。但我们可以继续使用MessageBox。根据是否已定义UNICODE标识符,MessageBox将实际调用MessageBoxA或是MessageBoxW。如下所示:
#ifdef UNICODE
#define MessageBox MessageBoxW
#else
#define MessageBox MessageBoxA
#endif
Windows的字符串函数
Microsoft C包含宽字符以及通用版的需要字符串参数的C语言运行库函数。不过,Windows复制了其中一部分C函数。例如,下面是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
Windows中不存在标准输入和标准输出的概念,所以我们可以在Windows程序中使用fprintf 函数,但不能使用printf 函数。sprintf 函数定义如下:
int sprintf ( char * szBuffer, const char * szFormat, ... );
第一个参数是一个字符串缓冲区;后面是一个格式字符串。使用sprintf 时,除了要考虑格式字符串与被格式化的变量不合外,还要考虑定义的字符串缓冲区必须足够大以存放结果。Microsoft 的专用函数 _snprintf 解决了这一问题,此函数引入一个参数来指定字符缓冲区的大小。
当需要对可变参数执行像printf 一样的格式化时,可以用vsprintf 来实现我们自己的函数。vsprintf只有三个参数,第三个参数是指向待格式化的参数数组的指针。
当然,随着宽字符的引入,sprintf系列的函数增加了许多,使得函数名让人困惑。下表列出了Microsoft版的C语言运行库和Windows所支持的所有Sprintf类函数。
ASCII | 宽字符 | 通用 | |
可变数目的参数 | |||
标准版 | sprintf | swprintf | _stprintf |
最大长度版 | _snprintf | _snwprintf | _sntprintf |
Windows版 | wsprintfA | wsprintfW | wsprintf |
参数数组的指针 | |||
标准版 | vsprintf | vswprintf | _vstprintf |
最大长度版 | _vsnprintf | _vsnwprintf | _vsntprintf |
Windows版 | wvsprintfA | wvsprintfW | wvprintf |
其中最大长度版(_sntprintf和_vsntprintf)声明在TCHAR.H头文件中
————————————————————— 我是快乐的分割线 —————————————————————
包含在windows.h头文件中
类型与宏:
TCHAR pString;
TEXT(pString);
如果有定义UNICODE,则以上定义为宽字符,否则为普通的但字节字符
函数总结:
iLength = lstrlen( pString ); //计算字符串长度
pString = lstrcpy( pString1, pString2 ); //复制字符串
pString = lstrcpyn( pString1, pString2, iCount ); //复制字符串
pString = lstrcat( pString1, pString2 ); //链接字符串
iComp = lstrcmp( pString1,pString2 ); //比较字符串
iComp = lstrcmpi( pString1,pString2 ); //比较字符串
iLength = wsprintf( szBuffer,"The sum of %i and %i is %i",5,3,5+3 ); //字符串格式化函数
iLength = wvsprintf( szBuffer, szFormat, pArgList ); //字符串格式化函数(参数数组的指针)