一直以来,我们将文本字符串编码成一组以0结尾的单字节字符,调用strlen,它就会返回“以0结尾的一个ANSI单字节字符数组”的字符数。
但是,某些语言文字系统(如韩文)的字符集有非常多的符号,一个字节最多能表示256个符号,这是远远不够的;为了支持这些语言文字系统,双字节字符集出现了。双字节字符集中,一个字符串中的每个字符都由1个或2个字节组成。
UTF全称是Unicode Transformation Format(Unicode转换格式),有以下几种格式。
C语言用char数据类型表示一个8位的ANSI字符。如下:
- char c = 'A';
-
- char buffer[100] = "string";
微软的C/C++编译器定义了一个内建的数据类型wchar_t,表示一个16位的Unicode(UTF-16)字符。默认情况下,在vs中新建一个项目时,编译器会打开/Zc:wchar_t的开关,我们才能够正常使用这个数据类型。
声明Unicode字符和字符串的方法。如下:
- wchar_t c = L'A';
-
- wchar_t buffer[100] = L"string";
字符串之前的大写字母L通知编译器该字符串应当编译为一个Unicode字符串。
为了与C语言区分,Windows希望定义了自己的数据类型。如下所示:
- typedef char CHAR;
-
- typedef wchar_t WCHAR; // wc, 16-bit UNICODE character
除此之外,还定义了大量方便的数据类型。如下所示:
- typedef CHAR *PCHAR, *PSTR;
- typedef CONST CHAR *PCSTR;
-
- typedef WCHAR *PWCHAR, *PWSTR;
- typedef CONST WCHAR *PCWSTR;
另外,在写代码的时候,可以使用ANSI或Unicode字符/字符串使能够通过编译。定义了以下类型的宏:
- #ifdef UNICODE // r_winnt
-
- typedef WCHAR TCHAR, *PTCHAR,PTSTR;
- typedef CONST WCHAR *PCTSTR;
-
- #define __TEXT(quote) L##quote // r_winnt
-
- #else
-
- typedef CHAR TCHAR, *PTCHAR,PTSTR;
- typedef CONST CHAR *PCTSTR;
-
- #define __TEXT(quote) quote // r_winnt
-
- #endif /* !_TCHAR_DEFINED */
-
-
- #define TEXT(quote) __TEXT(quote)
利用这些类型和宏,无论使用ANSI还是Unicode字符,都能通过编译。
- TCHAR c = TEXT('A');
-
-
- TCHAR buffer[100] = TEXT("string");
如果一个Windows函数的参数列表中有字符串,则通常有两个版本。例如:CreateFile
- WINBASEAPI
- HANDLE
- WINAPI
- CreateFileA(
- _In_ LPCSTR lpFileName,
- _In_ DWORD dwDesiredAccess,
- _In_ DWORD dwShareMode,
- _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
- _In_ DWORD dwCreationDisposition,
- _In_ DWORD dwFlagsAndAttributes,
- _In_opt_ HANDLE hTemplateFile
- );
-
- WINBASEAPI
- HANDLE
- WINAPI
- CreateFileW(
- _In_ LPCWSTR lpFileName,
- _In_ DWORD dwDesiredAccess,
- _In_ DWORD dwShareMode,
- _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
- _In_ DWORD dwCreationDisposition,
- _In_ DWORD dwFlagsAndAttributes,
- _In_opt_ HANDLE hTemplateFile
- );
CreateFileA:接受ANSI字符串。
CreateFileW:接受Unicode字符串。W代表wide
返回字符串长度的函数:
- #ifdef UNICODE
- #define _tcslen wcslen
- #else
- #define _tcslen strlen
- #endif
现有的字符串处理函数如_tcscpy等已被标记位废弃不用。现在它们都对应了一个新版本的函数,前面名称相同,最后加了一个_s后缀。示例:
- TCHAR before[3] = { TEXT('A'),TEXT('B') ,'\0'};
- TCHAR after[3] = { TEXT('-'),TEXT('-') ,'\0'};
-
- errno_t result = _tcscpy_s(after, _countof(after), before);
除了新的安全字符串函数,C运行库还新增了一些函数,用于在执行字符串处理时提供更多控制。自然,这些函数提供了ANSI(A)版本和Unicode(W)版本。
- StringCchCat(PTSTR pszDest, size_t cchDest, PTSTR pszSrc)
-
- StringCchCopy(PTSTR pszDest, size_t cchDest, PTSTR pszSrc)
在这些方法中,都含有一个“Cch”,这表示Count of characters,即字符数;通常使用_countof来获取值。
另外还有一系列的名称中含有“Cb”的函数,比如StringCbCat、StringCbCopy,这些函数要求用字节数来指定大小,通常使用sizeof来获取值。
还有一些末尾加了Ex的函数,表示可扩展的版本,例如StringCchCatEx、StringCchCopyEx。
最好将应用程序转换为支持Unicode的形式。
以下为使用Unicode的好处:
- //将多字节的字符串转换为宽字符字符串
- MultiByteToWideChar(
- _In_ UINT CodePage,
- _In_ DWORD dwFlags,
- _In_NLS_string_(cbMultiByte) LPCCH lpMultiByteStr,
- _In_ int cbMultiByte,
- _Out_writes_to_opt_(cchWideChar,return) LPWSTR lpWideCharStr,
- _In_ int cchWideChar
- );
-
- //将宽字符字符串转换为多字节字符串
- WideCharToMultiByte(
- _In_ UINT CodePage,
- _In_ DWORD dwFlags,
- _In_NLS_string_(cchWideChar) LPCWCH lpWideCharStr,
- _In_ int cchWideChar,
- _Out_writes_bytes_to_opt_(cbMultiByte,return) LPSTR lpMultiByteStr,
- _In_ int cbMultiByte,
- _In_opt_ LPCCH lpDefaultChar,
- _Out_opt_ LPBOOL lpUsedDefaultChar
- );