关于 Windows 系统下编程之字符处理
我们在widows 下编程时,经常遇到关于字符串的处理的问题,特别是在用VC 编程时,我们经常犯错误,所以我结合我的编程经验和我学习widows 核心编程这本书后的体会,来自己作一个总结。
1 、字符集
在我们平时的编代码的过程中,总在不但地处理各种关于字符集的问题。对于我们来说,习惯于将文本串作为一系列单字节字符串来处理,并且在结尾加上”/0” ,它用来标识串的结束。当我们调用strlen(…) 函数时,它在以 0 结尾的单字节字符数组中返回字符的数目。
2 、单字节与双字节字符集
一个字节占用8 位(bit) 。而双字节字符集中,字符串中的每个字符在编码时可以包含一个字节或包含两个字节(这时strlen(…) 函数就不能告诉我们字符串中究竟有多少个字符,它只能告诉我们在该字符串中的第一个”/0” 之前有多少个字节。在VC 中,我们可以用_mbslen(…) 来代替。)。
3 、Unicode 字符集
Unicode 字符集提供了简单而有一致的表示字符串的方法,它规定字符串中每一个字符编码都是16 位( 两个字节) 。所以利用Unicode 字符集可以编码216 = 65535 个字符。这样,它就能够对世界各国的书面文字中的所有字符进行编码,远远超过了单字节字符集的 2 5 6 个字符的数目。
部分 Unicode 区域字符表
1 6 位代码
字符
16 位代码
字符
0 0 0 0 - 0 0 7 F
A S C I I
0 3 0 0 - 0 3 6 F
通用区分标志
0 0 8 0 - 0 0 F F
拉丁文1 字符
0 4 0 0 - 0 4 F F
西里尔字母
0 1 0 0 - 0 1 7 F
欧洲拉丁文
0 5 3 0 - 0 5 8 F
亚美尼亚文
0 1 8 0 - 0 1 F F
扩充拉丁文
0 5 9 0 - 0 5 F F
西伯莱文
0 2 5 0 - 0 2 A F
标准拼音
0 6 0 0 - 0 6 F F
阿拉伯文
0 2 B 0 - 0 2 F F
修改型字母
0 9 0 0 - 0 9 7 F
梵文
4 、windows 与Unicode
我们基本上天天和Windows打交道,知道Windows 2000/XP 是用Unicode进行开发的。在Windows 2000/XP 中用于创建窗口、呈现文本、进行字符串操作等所有核心函数都需要Unicode字符串。在平时我们 调用任何一个 Windows 函数并给它传递一个ANSI字符串,那么系统首先要将字符串转换成 Unicode ,然后将 Unicode 字符串传递给操作系统。如果希望函数返回ANSI字符串,系统就会首先将 Unicode 字符串转换成ANSI字符串,然后将结果返回给你的应用程序。所有这些转换操作都是在你看不见的情况下发生的。当然,进行这些字符串的转换需要占用系统的时间和内存。
例如,如果调用CreateWindowEx 函数,并传递类名字和窗口标题文本的非Unicode 字符串,那么CreateWindowEx 必须分配内存块(在你的进程的默认堆中),将非Unicode 字符串转换成Unicode 字符串,并将结果存储在分配到的内存块中,然后调用Unicode 版本的CreateWindowEx 函数。对于用字符串填入缓存的函数来说,系统必须首先将 Unicode 字符串转换成非 Unicode 字符串,然后你的应用程序才能处理该字符串。由于系统必须执行所有这些转换操作,因此你的应用程序需要更多的内存,并且运行的速度比较慢。通过从头开始用 Unicode 来开发应用程序,就能够使你的应用程序更加有效地运行 。
5 、编写 Unicode 源代码
① C 运行期库对 Unicode 的支持
为了利用 Unicode 字符串,定义了一些数据类型,如我们熟悉的 wchar_t
typedef unsigned short wchar_t ;
eg : wchar_t szBuffer[100] ;// 在内存中的大小为 100*18bit = 200 byte 。可以存储最多为 9 9 个字符的 Unicode 字符串和一个结尾为零的字符
现在一些标准的 ANSI 的 C 字符串处理函数都有相应的等价的 Unicode 函数。如:
eg :char * strcat(char *,const char *);
char_t * wcscat(wchar_t *,const wchar_t *)
标准的ANSI C 字符串函数和它们的等价Unicode函数
char * strchr(const char *,int);
wchar_t * wcschr(const wchar_t *,wchar_t);
int strcmp(const char *,const char *);
int wcscmp(const wchar_t *,const wchar_t *);
char * strcpy(char *,const char *);
wchar_t * wcscpy(wchar_t *,const wchar_t *);
size_t strlen(const char *);
size_t wcslen(const wchar_t *);
注意:所有的 Unicode 函数均是以 wcs 开头,而 ANSI 函数则以 str 开头
②Tchar,h头文件
Tchar.h 文件的唯一作用是帮助创建ANSI/Unicode通用源代码文件。它包含你应该用在源代码中的一组宏,而不应该直接调用str函数或者wcs函数。如果在编译源代码文件时定义了UNICODE,这些宏就会引用wcs这组函数。如果没有定义_UNICODE,那么这些宏将引用str这组宏。
A WCHAR if UNICODE is defined, a CHAR otherwise
#ifdef UNICODE
typedef WCHAR TCHAR;
#else typedef char TCHAR;
#endif
所以根据上面的定义我们可以将TCHAR看为通用的字符类型。
eg : TCHAR szBuffer[100];
TCHAR ch = “Hello”;
注意: TCHAR ch = “Hello” 这行代码在Visual C++中,如果没有定义_UNICODE,编译是按ANSI编码,可以通过(在VC中默认是ANSI编码)。但当我们定义了_UNICODE时,编译 就通不过,我们必须改成TCHAR ch = L“Hello”。 字符串( literal string )前面的大写字母 L ,用于告诉编译器该字符串应该作为 Unicode 字符串来编译。当编译器将字符串置于程序的数据部分中时,它在每个字符之间分散插入零字节。这种变更带来的问题是,现在只有当定义了 _UNICODE 时,程序才能成功地进行编译。我们需要另一个宏,以便有选择地在字符串的前面加上大写字母 L 。这项工作由 _TEXT 宏来完成, _TEXT 宏也在 T C h a r. h 文件中做了定义。
如果定义了 _UNICODE ,那么 _TEXT 定义为下面的形式:
#define_TEXT(x) __T(x)
#define__T(x) L## x
如果没有定义 _UNICODE ,那么 _TEXT 定义为下面的形式:
#define _TEXT(x) x
所以 使用该宏,可以改写上面这行代码为 TCHAR ch = _TEXT(“Hello”) ,这样,无论是否定义了 _UNICODE 宏,它都能够正确地进行编译。
③ Windows 定义的 Unicode 数据 ( This type is declared in WinNT.h )
Typedef wchar_t WCHAR; //16-bit Unicode Characters
Typedef WCHAR *PWSTR;//Pointer to a new null-terminated //string of 16-bitUnicode characters
Typedef CONST WCHAR *PCWSTR;//Pointer to a constant //null-terminated string of //16-bit Unicode characters
A PWSTR if UNICODE is defined, a PSTR otherwise
#ifdef UNICODE
typedef LPWSTR PTSTR;
#else
typedef LPSTR PTSTR;
#endif
A PCWSTR if UNICODE is defined, a PCSTR otherwise.
#ifdef UNICODE
typedef LPCWSTR PCTSTR;
#else
typedef LPCSTR PCTSTR;
#endif
④ windows 中的 Unicode 函数和 ANSI 函数
前面我说过,在 Windows 中有好多的函数有两个版本,一个接受 Unicode 字符串,一个接受 ANSI 串,如:
#ifdefUNICODE
#defineCreateWindowEx CreateWindowExW
#else
#define CreateWindowEx CreateWindowExA
#endif // !UNICODE
当编译源代码模块时,UNICOD 是否已经作了定义,将决定你调用的是哪个CreateWindowEx 版本。当转用一个1 6 位的Windows 应用程序时,你在编译期间可能没有定义UNICODE 。对CreateWindowEx 函数的任何调用都会将该宏扩展为对CreateWindowExA 的调用,即对CreateWindowEx 的A N S I 版本的调用。由于1 6 位Wi n d o w s 只提供了C r e a t e Wi n d o w s E x 的A N S I 版本,因此可以比较容易地转用它的应用程序。如果定义了UNICODE ,则是对CreateWindowExW 调用。
⑤在 Unicode 与 ANSI 之间转换字符串
MultiByteToWideChar(…) 用于将多字节字符串转换成宽字节字符串
int MultiByteToWideChar (
UINT CodePage , // 标识一个与多字节字符串相关的代码页号
DWORD dwFlags , // Flags indicating the conversion type
LPCSTR lpMultiByteStr , // 要转换的字符串指针
int cbMultiByte , // 用与指明源串的长度 ( 以字节计算 ), 如果传值为 -1 , // 那么函数返回源串的长度 ; 为 0 ,则失败
LPWSTR lpWideCharStr ,// 目标串的地址
int cchWideChar // 目标串的长度 ( 以自己计算 ), 如果传值为 0 ,则函数不 // 执行转换,而是返回使转换成功所需要的缓存的值 .
);
WideCharToMultiByte ( … )用于将宽字节字符串转换成等价的多字节字符串
int WideCharToMultiByte(
UINT CodePage ,
DWORD dwFlags ,
LPCWSTR lpWideCharStr ,
int cchWideChar ,
lpMultiByteStr ,
int cbMultiByte ,
LPCSTR lpDefaultChar ,
LPBOOL lpUsedDefaultChar );
由于和 MultiByteToWideChar(…) 差不多,所以可以对比着看 MSDN
有错误欢迎大家指正,请发邮件到 Leezhm@126.com
<a title="Vdoing StatsX No.54977" href="http://www.vdoing.com"><img src="http://simg.vdoing.com/m/54977/x01.gif?noscript" border="0" alt="" /></a>