第一部分,必备知识
第2章 字符和字符串处理
概述: Windows支持ANSI和Unicode两种字符集,自Windows NT起内核全部采用Unicode实现。编写Windows程序时应使用Unicode以支持多语言和国际化,并通过通用类型和宏实现代码兼容性。
字符编码:ANSI与Unicode的区别
char c = 'A';
wchar_t w = L'A';
|
#ifdef UNICODE typedef wchar_t TCHAR; typedef wchar_t* PTSTR; typedef const wchar_t* PCTSTR; #define TEXT(quote) L##quote #else typedef char TCHAR; typedef char* PTSTR; typedef const char* PCTSTR; #define TEXT(quote) quote #endif
TCHAR szBuffer[100]; PTSTR pszString = szBuffer; PCTSTR pszConst = TEXT("Hello");
#ifdef UNICODE #define CreateFile CreateFileW #define MessageBox MessageBoxW #else #define CreateFile CreateFileA #define MessageBox MessageBoxA #endif
|
Windows通用字符类型与宏
为了编写可同时支持ANSI和Unicode的代码,Windows头文件定义了一系列通用类型和宏。通过定义或取消定义UNICODE宏,可以控制TCHAR等类型映射到char还是wchar_t。TEXT宏会根据UNICODE定义自动添加L前缀。Windows API函数也提供了类似的宏机制,如CreateFile在Unicode下实际调用CreateFileW,在ANSI下调用CreateFileA。
#ifdef UNICODE typedef wchar_t TCHAR; typedef wchar_t* PTSTR; typedef const wchar_t* PCTSTR; #define TEXT(quote) L##quote #else typedef char TCHAR; typedef char* PTSTR; typedef const char* PCTSTR; #define TEXT(quote) quote #endif
TCHAR szBuffer[100]; PTSTR pszString = szBuffer; PCTSTR pszConst = TEXT("Hello");
#ifdef UNICODE #define CreateFile CreateFileW #define MessageBox MessageBoxW #else #define CreateFile CreateFileA #define MessageBox MessageBoxA #endif
|
C运行库中的安全字符串函数
传统的C字符串函数(如strcpy、strcat)不检查目标缓冲区大小,容易导致缓冲区溢出漏洞。C运行库提供了带_s后缀的安全版本,要求显式指定目标缓冲区大小。Windows还提供了StringCch和StringCb系列函数,它们返回HRESULT以便进行错误处理,并且明确区分字符计数(cch)和字节计数(cb)。这些函数在缓冲区不足时会安全地截断字符串,而不是造成溢出。
char dest[10]; strcpy(dest, "This is a very long string");
errno_t strcpy_s(char* dest, size_t destSize, const char* src); errno_t strcat_s(char* dest, size_t destSize, const char* src); int sprintf_s(char* buffer, size_t sizeOfBuffer, const char* format, ...);
char dest[10]; strcpy_s(dest, _countof(dest), "short");
HRESULT StringCchCopy(LPTSTR pszDest, size_t cchDest, LPCTSTR pszSrc); HRESULT StringCchCat(LPTSTR pszDest, size_t cchDest, LPCTSTR pszSrc); HRESULT StringCchPrintf(LPTSTR pszDest, size_t cchDest, LPCTSTR pszFormat, ...);
HRESULT StringCbCopy(LPTSTR pszDest, size_t cbDest, LPCTSTR pszSrc);
TCHAR szPath[MAX_PATH]; StringCchCopy(szPath, _countof(szPath), TEXT("C:\\Windows\\System32")); StringCchCat(szPath, _countof(szPath), TEXT("\\kernel32.dll"));
|
ANSI与Unicode字符串的转换
在实际编程中,有时需要在ANSI和Unicode之间进行转换,例如与遗留系统交互或处理特定文件格式。Windows提供了MultiByteToWideChar和WideCharToMultiByte两个函数进行转换。MultiByteToWideChar将多字节字符(如ANSI)转换为宽字符(Unicode),WideCharToMultiByte则相反。这两个函数都支持指定代码页(如CP_ACP表示当前ANSI代码页),并且可以处理无法转换的字符。
int MultiByteToWideChar( UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte, LPWSTR lpWideCharStr, int cchWideChar );
int WideCharToMultiByte( UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar, LPBOOL lpUsedDefaultChar );
char szANSI[] = "Hello, 世界"; wchar_t szUnicode[100]; int nLen = MultiByteToWideChar(CP_ACP, 0, szANSI, -1, szUnicode, 100);
|
总结:
编写Windows程序应始终使用Unicode字符集以支持国际化和直接调用NT内核。使用通用类型(TCHAR, PTSTR, PCTSTR)和TEXT宏编写可同时支持ANSI和Unicode的代码。避免使用传统C字符串函数,改用安全版本(_s后缀)或Windows特有的StringCch/StringCb系列函数。始终显式指定缓冲区大小,使用_countof宏计算元素个数。进行ANSI和Unicode转换时使用MultiByteToWideChar和WideCharToMultiByte函数。