如何使用 Win32 WCHAR 进行字符串操作
How to do string operations with Win32 WCHAR
我有一个 win32 项目,我在其中尝试使用自定义函数编辑 WCHAR 字符串的字符。
我知道这代表 Wide Char 并且是 Unicode,但是我不完全了解编码的工作原理。例如,我知道 UTF-8 也包含 Unicode,但它与 WCHAR 相同吗?
我假设字符串看起来像
00 43 00 4f 00 44 00 45 00 00
C O D E [=10=]
对于复制来说,假设字符串的长度是原来的两倍就可以正常工作。但是,我在搜索字符时遇到错误,例如:
for(int i = wcslen(inStr) - 2; i >= 0; i--) {
WCHAR current[] = {inStr[i], inStr[i + 1], 0, 0};
if(current == _T("/")) {
pos = i;
break;
}
}
产生一些损坏的错误。我是不是太复杂了?我知道可能有很多功能可以做到这一点,但我想了解它是如何工作的,这样我就可以编写高效的代码。谢谢
更简短的回答
您遇到的具体问题是 current[n]
是数组中的第 n 个 元素 ,而不是第 n 个 byte 的数组。像 current + n
这样的指针运算也会给你 current
指向的第 n 个 元素 。如果您声明一个 int
、double
、一些 struct
或其他任何数组,情况也是如此。
所以,当你声明一个数组 wchar_t a[] = L"!"
,然后取 wcslen(a)
,你会得到数组中宽字符的计数,1。如果你尝试设置 i = wcslen(a) - 2;
然后取a[i]
,i
会是-1,这是一个严重的bug。
更长的解释
在Windows上,WCHAR
是标准类型wchar_t
的别名。你不会说你是用 C 还是 C++ 编写的。在 <wchar.h>
和 <wctype.h>
中,C 标准库中有许多函数可以处理宽字符字符串。 C++ 标准库拥有所有这些,以及 <string>
中的 std::wstring
和包括 std::wcout
、std::wcin
和 std::wcerr
的宽字符流(尽管 Windows 并不完全支持它们)。大多数 Windows API 函数也可以接受宽字符字符串。宽字符串的标准类型是 wchar_t*
,但 WCHAR*
、LPWSTR
以及 Visual Studio、TCHAR*
和 [=35 的现代版本默认情况下=] 也可以。
在 Windows 上,宽字符是小端 UTF-16。这不是可移植的,但是 WCHAR
也不是。在其他一些系统上,宽字符要么是大端 UTF-16,要么是大端或小端 UTF-32。在 C 中,标准类型 char16_t
和 char32_t
在 <uchar.h>
中定义。在 C++ 中,它们内置于语言中。如果您尝试将 char16_t*
传递给需要 wchar_t*
的函数,则在没有强制转换的情况下或在 Windows 以外的目标上根本无法工作。
UTF-8 是一种存储与七位 ASCII 向后兼容的 Unicode 代码点的方法。 UTF-8 是 UTF-16 或 UTF-32 的另一种表示形式。一个 UTF-8 字符串将存储在一个 unsigned char
或 char
的数组中,一个 Unicode 代码点可能需要几个字节来存储它。实际上,由于代理项对,一个 Unicode 代码点也可能需要两个 UTF-16 对象来对其进行编码。有时使用不同的表示形式很方便(UTF-16LE 是 Windows ABI 所期望的,也是 ICU 和 QT 等库在内部使用的,而 UTF-32 是保证所有 Unicode 字符的唯一表示形式将适合单个元素),但我的建议是尽可能使用 UTF-8,必要时使用其他编码。
可能的解决方案
如果你想通过一个宽字符串向后阅读,你可以试试这个:
int i = wcslen(inStr); // Could be 0.
if (i > 0) { // Don't read one element past the start of the array.
do {
--i;
} while ( i > 0 && inStr[i] != L'/' );
}
/* When we reach this line, i is either 0 or the index of the last slash
* in inStr, which could also be 0. We can test whether inStr[i] == L'/' or
* write an if() within our loop to do something more complicated.
*/
我有一个 win32 项目,我在其中尝试使用自定义函数编辑 WCHAR 字符串的字符。
我知道这代表 Wide Char 并且是 Unicode,但是我不完全了解编码的工作原理。例如,我知道 UTF-8 也包含 Unicode,但它与 WCHAR 相同吗?
我假设字符串看起来像
00 43 00 4f 00 44 00 45 00 00
C O D E [=10=]
对于复制来说,假设字符串的长度是原来的两倍就可以正常工作。但是,我在搜索字符时遇到错误,例如:
for(int i = wcslen(inStr) - 2; i >= 0; i--) {
WCHAR current[] = {inStr[i], inStr[i + 1], 0, 0};
if(current == _T("/")) {
pos = i;
break;
}
}
产生一些损坏的错误。我是不是太复杂了?我知道可能有很多功能可以做到这一点,但我想了解它是如何工作的,这样我就可以编写高效的代码。谢谢
更简短的回答
您遇到的具体问题是 current[n]
是数组中的第 n 个 元素 ,而不是第 n 个 byte 的数组。像 current + n
这样的指针运算也会给你 current
指向的第 n 个 元素 。如果您声明一个 int
、double
、一些 struct
或其他任何数组,情况也是如此。
所以,当你声明一个数组 wchar_t a[] = L"!"
,然后取 wcslen(a)
,你会得到数组中宽字符的计数,1。如果你尝试设置 i = wcslen(a) - 2;
然后取a[i]
,i
会是-1,这是一个严重的bug。
更长的解释
在Windows上,WCHAR
是标准类型wchar_t
的别名。你不会说你是用 C 还是 C++ 编写的。在 <wchar.h>
和 <wctype.h>
中,C 标准库中有许多函数可以处理宽字符字符串。 C++ 标准库拥有所有这些,以及 <string>
中的 std::wstring
和包括 std::wcout
、std::wcin
和 std::wcerr
的宽字符流(尽管 Windows 并不完全支持它们)。大多数 Windows API 函数也可以接受宽字符字符串。宽字符串的标准类型是 wchar_t*
,但 WCHAR*
、LPWSTR
以及 Visual Studio、TCHAR*
和 [=35 的现代版本默认情况下=] 也可以。
在 Windows 上,宽字符是小端 UTF-16。这不是可移植的,但是 WCHAR
也不是。在其他一些系统上,宽字符要么是大端 UTF-16,要么是大端或小端 UTF-32。在 C 中,标准类型 char16_t
和 char32_t
在 <uchar.h>
中定义。在 C++ 中,它们内置于语言中。如果您尝试将 char16_t*
传递给需要 wchar_t*
的函数,则在没有强制转换的情况下或在 Windows 以外的目标上根本无法工作。
UTF-8 是一种存储与七位 ASCII 向后兼容的 Unicode 代码点的方法。 UTF-8 是 UTF-16 或 UTF-32 的另一种表示形式。一个 UTF-8 字符串将存储在一个 unsigned char
或 char
的数组中,一个 Unicode 代码点可能需要几个字节来存储它。实际上,由于代理项对,一个 Unicode 代码点也可能需要两个 UTF-16 对象来对其进行编码。有时使用不同的表示形式很方便(UTF-16LE 是 Windows ABI 所期望的,也是 ICU 和 QT 等库在内部使用的,而 UTF-32 是保证所有 Unicode 字符的唯一表示形式将适合单个元素),但我的建议是尽可能使用 UTF-8,必要时使用其他编码。
可能的解决方案
如果你想通过一个宽字符串向后阅读,你可以试试这个:
int i = wcslen(inStr); // Could be 0.
if (i > 0) { // Don't read one element past the start of the array.
do {
--i;
} while ( i > 0 && inStr[i] != L'/' );
}
/* When we reach this line, i is either 0 or the index of the last slash
* in inStr, which could also be 0. We can test whether inStr[i] == L'/' or
* write an if() within our loop to do something more complicated.
*/