作者:shenzi
链接:http://hi.csdn.net/shenzi
Windows核心编程:字符和字符串处理
1.字符编码
ANSI 字符,一个字符一字节(8位),最多只能表达256个字符。
UTF 的全称是Unicode Transformation Format(Unicode转换格式)。
UTF 的全称是Unicode Transformation Format(Unicode转换格式)。
UTF-16 将 每个字符编码为两个字符(16位)Windows使用的Unicode使用UTF-16编码,因为全球各地使用的大部分语言很容易用一个16位值来表示。 但是对于某些语言16位还难以表示所有字符,对于这些语言,UTF-16支持使用代理(surrogate),用32位(四字节)来表示一个字符。 UTF-16在节省空间和简化编码者两个目标之间,提供了很好的折衷。
.NET Framework始终使用UTF-16来编码所有字符和字符串,所以在开发Windows应用程序中,如果需要在本机代码(native code)和托管代码(managed code)之间传递字符或字符串,使用UTF-16能改进性能和减少内存消耗。
UTF-8 将一些字符编码为1个字节,一些字符编码为2个字节,一些字符编码为3个字节,一些字符编码为4个字节。
UTF-32 将每个字符编码为4个字节。
.NET Framework始终使用UTF-16来编码所有字符和字符串,所以在开发Windows应用程序中,如果需要在本机代码(native code)和托管代码(managed code)之间传递字符或字符串,使用UTF-16能改进性能和减少内存消耗。
UTF-8 将一些字符编码为1个字节,一些字符编码为2个字节,一些字符编码为3个字节,一些字符编码为4个字节。
UTF-32 将每个字符编码为4个字节。
2.字符串数据类型
char: 表示一个8位ANSI字符;
wchar_t: 表示一个16位Unicode(UTF-16)字符;
(Microsoft C/C++编译器内建的数据类型,编译器只有指定了/Zc:wchar_t,才会定义这个数据类型。Visual Studio 新建一个C++项目这个编译器开关时制定的。这样才能借助于编译器天生就能理解的内建基元类型来更好的操作 Unicode字符。)
typedef unsigned short wchar_t; //wchar_t定义为无符号短整型;
// A 16-bit character
wchar_t c = L'A';
// An array up to 99 16-bit characters and a 16-bit terminating zero.
wchar_t szBuffer[100] = L"A String";
字符串之前加大写“L”是告诉编译器该字符串应当编译为一个Unicode字符串。
Windows开发团队定义了自己的数据类型。
//WinNT.h
typedef char CHAR; // An 8-bit character
typedef wchar_t WCHAR; // A 16-bit character
//Pointer to 8-bit character(s)
typedef CHAR *PCHAR;
typedef CHAR *PSTR;
typedef CONST CHAR *PCSTR
//Pointer to 16-bit character(s)
typedef WCHAR *PWCHAR;
typedef WCHAR *PWSTR;
typedef CONST WCHAR *PCWSTR;
#ifdef UNICODE
typedef WCHAR TCHAR, *PTCHAR, PTSTR;
typedef CONST WCHAR *PCTSTR;
#define __TEXT(quote) quote // r_winnt
#define __TEXT(quote) L##quote
#else
typedef CHAR TCHAR, *PTCHAR, PTSTR;
typedef CONST CHAR *PCTSTR;
#define __TEXT(quote) quote
#endif
#define TEXT(quote) __TEXT(quote)
利用以上类型和宏来写代码,无论使用ANSI还是Unicode字符,它都能通过编译。如下:
//如果UNICODE被定义, 则为16位字符; 否则为8位字符
TCHAR c = TEXT('A');
// 如果UNICODE被定义, 则为16位字符; 否则为8位字符
TCHAR szBuffer[100] = TEXT("A String");
wchar_t: 表示一个16位Unicode(UTF-16)字符;
(Microsoft C/C++编译器内建的数据类型,编译器只有指定了/Zc:wchar_t,才会定义这个数据类型。Visual Studio 新建一个C++项目这个编译器开关时制定的。这样才能借助于编译器天生就能理解的内建基元类型来更好的操作 Unicode字符。)
typedef unsigned short wchar_t; //wchar_t定义为无符号短整型;
// A 16-bit character
wchar_t c = L'A';
// An array up to 99 16-bit characters and a 16-bit terminating zero.
wchar_t szBuffer[100] = L"A String";
字符串之前加大写“L”是告诉编译器该字符串应当编译为一个Unicode字符串。
Windows开发团队定义了自己的数据类型。
//WinNT.h
typedef char CHAR; // An 8-bit character
typedef wchar_t WCHAR; // A 16-bit character
//Pointer to 8-bit character(s)
typedef CHAR *PCHAR;
typedef CHAR *PSTR;
typedef CONST CHAR *PCSTR
//Pointer to 16-bit character(s)
typedef WCHAR *PWCHAR;
typedef WCHAR *PWSTR;
typedef CONST WCHAR *PCWSTR;
#ifdef UNICODE
typedef WCHAR TCHAR, *PTCHAR, PTSTR;
typedef CONST WCHAR *PCTSTR;
#define __TEXT(quote) quote // r_winnt
#define __TEXT(quote) L##quote
#else
typedef CHAR TCHAR, *PTCHAR, PTSTR;
typedef CONST CHAR *PCTSTR;
#define __TEXT(quote) quote
#endif
#define TEXT(quote) __TEXT(quote)
利用以上类型和宏来写代码,无论使用ANSI还是Unicode字符,它都能通过编译。如下:
//如果UNICODE被定义, 则为16位字符; 否则为8位字符
TCHAR c = TEXT('A');
// 如果UNICODE被定义, 则为16位字符; 否则为8位字符
TCHAR szBuffer[100] = TEXT("A String");
3.Windows中的Unicode
函数
和ANSI
函数
自Windows NT起,Windows的所有版本都完全用Unicode构建 。
也 就是说,所有核心函数(创建窗口,显示文本,进行字符串处理等等)都需要Unicode字符串。调用Windows函数时,如果向它传入一个ANSI字符 串,那么函数首先会把字符串转换为Unicode,再把结果传给操作系统。如果希望函数返回ANSI字符串,那么操作系统会先把Unicode字符串转换 为ANSI字符串,再把结果返回给我们的应用程序。为了执行这些字符串转换,系统会产生时间和内存上的开销。
#ifdef UNICODE
#define CreateWindowEx CreateWindowExW
#else
#define CreateWindowEx CreateWindowExA
#endif
Visual Studio创建一个新项目时默认会定义UNICODE,所以调用 CreateWindowExW ,即Unicode版函数。ANSI版函数 CreateWindowExA ,只是一个转换层,它负责分配内存,以便将ANSI字符串转换为Unicode字符串,然后再调用 Unicode版函数 CreateWindowExW。所以为了减少时间和内存上的 耗费,使用Unicode来开发程序。
所有需要字符串作为参数的COM借口方法都只接受Unicode字符串。
#ifdef UNICODE
#define CreateWindowEx CreateWindowExW
#else
#define CreateWindowEx CreateWindowExA
#endif
Visual Studio创建一个新项目时默认会定义UNICODE,所以调用 CreateWindowExW ,即Unicode版函数。ANSI版函数 CreateWindowExA ,只是一个转换层,它负责分配内存,以便将ANSI字符串转换为Unicode字符串,然后再调用 Unicode版函数 CreateWindowExW。所以为了减少时间和内存上的 耗费,使用Unicode来开发程序。
所有需要字符串作为参数的COM借口方法都只接受Unicode字符串。
4.C运行库中的Unicode和ANSI
为了使源代码既能用ANSI编译,又能用Unicode编译TChar.h定义了一下宏:
#ifdef _UNICODE
#define _tcslen wcslen
#else
#define _tcslen strlen
#endif
在应用程序中应确保同时定义了UNICODE和_UNICODE
#ifdef _UNICODE
#define _tcslen wcslen
#else
#define _tcslen strlen
#endif
在应用程序中应确保同时定义了UNICODE和_UNICODE
5.为何要用Unicode
- Unicode有利于应用程序的本地化;
- 使用 Unicode,只需发布一个二进制(.exe或.dll)文件,即可支持所有语言;
- Unicode提升应用程序的效率,代码执行更快,占用内存更少。Windows内部的一切工作都是使用 Unicode字符和 Unicode字符串来进行的。假如我们坚持使用ANSI字符或字符串,Windows将被迫分配内存,将字符或字符串转换为Unicode形式;
- 使用 Unicode,应用程序能够轻松的调用尚未弃用的Windows函数,因为一些Windows函数提供的版本只能处理 Unicode字符和字符串;
- 使用 Unicode,应用程序的代码很容易与COM集成(要求使用 Unicode );
- 使用 Unicode,应用程序的代码很容易与.NET Framework集成(要求使用 Unicode );
- 使用 Unicode,能保证应用程序的代码能够轻松操纵我们自己的资源(其中的字符串总是 Unicode形式 )。
- 开始将文本字符串想象为字符的数组,而不是 char 或字节的数组;
- 用通用数据类型(如 TCHAR/PTSTR )来表示文本字符和字符串;
- 用明确的数据类型(如 BYTE 和 PBYTE )来表示字节,字节指针和数据缓冲区;
- 用 TEXT 或 _T 宏来表示字面量字符和字符串,但为了保持一致性和更好的可读性,请避免两者混用;
- 执行全局替换(例如用 PTSTR 替换 PSTR ) ;
- 修改与字符串有关的计算。例如,函数经常希望我们传给它缓冲区打小的字符数,而不是字节数。这意味着我们应该传入 _countoof(szBuffer), 而不是 sizeof(szBuffer) 。而且,如果需要为一个字符串分配一个内存块,而且知道字符串中的字符数,那么要 记住内存是以字节来分配的 。
- 避免使用 printf 系列函数,正确的做法是使用 MultiByteToWideChar 和 WideCharToMultiByte 函数。
- Unicode和_ Unicode符号要么同时指定,要么都不指定。
对于字符串处理函数,应该遵循以下基本准则:
- 始终使用安全的字符串处理函数,比如后缀为 _s 的函数,或者前缀为 StringCch 的函数。后者主要在我们想明确控制截断的时候使用了如果不像明确控制截断,首选前者。
- 利用 /GS 和 /RTCs 编译器标志来自动检测缓冲区溢出。
- 不要使用 Kernel32 方法来进行字符串处理,比如 lstrcat 和 lstrcpy 。
- 在应用程序的代码中,需要比较两种字符串,应使用 CompareStringOrdinal 来进行比较。
UINT uCodePage , // code page
DWORD dwFlags , // character-type options
PCSTR pMultiByteStr , // string to map
int cbMultiByte , // number of bytes in string
PWSTR pWideCharStr , // wide-character buffer
int cchWideChar // size of buffer
);
- 调用 MultiByteToWideChar,为 pWideCharStr 传入 NULL,为cchWideChar参数传入0,为cbMultiByte参数传入-1;
- 分配一块足以容纳转换后的Unicode字符串的内存。它的大小是一个 MultiByteToWideChar 调用的返回值乘以sizeof(wchar_t);
- 再次调用 MultiByteToWideChar ,这一次将缓冲区地址作为pWideCharStr参数的值传入,将第一次 MultiByteToWideChar 调用的返回值乘以sizeof(wchar_t)后得到的大小作为cchWideChar参数的值传入;
- 使用转换后的字符串;
- 释放Unicode字符串占用的内存块。