如何使用 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 个 元素 。如果您声明一个 intdouble、一些 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::wcoutstd::wcinstd::wcerr 的宽字符流(尽管 Windows 并不完全支持它们)。大多数 Windows API 函数也可以接受宽字符字符串。宽字符串的标准类型是 wchar_t*,但 WCHAR*LPWSTR 以及 Visual Studio、TCHAR* 和 [=35 的现代版本默认情况下=] 也可以。

在 Windows 上,宽字符是小端 UTF-16。这不是可移植的,但是 WCHAR 也不是。在其他一些系统上,宽字符要么是大端 UTF-16,要么是大端或小端 UTF-32。在 C 中,标准类型 char16_tchar32_t<uchar.h> 中定义。在 C++ 中,它们内置于语言中。如果您尝试将 char16_t* 传递给需要 wchar_t* 的函数,则在没有强制转换的情况下或在 Windows 以外的目标上根本无法工作。

UTF-8 是一种存储与七位 ASCII 向后兼容的 Unicode 代码点的方法。 UTF-8 是 UTF-16 或 UTF-32 的另一种表示形式。一个 UTF-8 字符串将存储在一个 unsigned charchar 的数组中,一个 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.
 */