11

我正在尝试在 Windows 中实现文本支持,以便以后也迁移到 Linux 平台。以统一的方式支持国际语言是理想的,但在考虑到所讨论的两个平台时,这似乎并不容易实现。我花了相当多的时间阅读 UNICODE、UTF-8(和其他编码)、widechars 等,这是我到目前为止所理解的:

UNICODE 作为标准,描述了可映射的字符集及其出现的顺序。我将其称为“什么”:UNICODE 指定可用的内容。

UTF-8(和其他编码)指定如何:每个字符将如何以二进制格式表示。

现在,在windows上,他们最初选择了UCS-2编码,但不符合要求,所以他们有UTF-16,必要时也是多字符。

所以这是两难境地:

  1. Windows 内部仅支持 UTF-16,因此如果您想支持国际字符,则必须转换为它们的 Widechar 版本以相应地使用操作系统调用。似乎不支持使用多字节 UTF-8 字符串调用 CreateFileA() 之类的东西,并让它看起来正确。这个对吗?
  2. 在 C 中,有一些支持多字节的函数(_mbscat、_mbscpy 等),但是在 Windows 上,这些函数的字符类型定义为 unsigned char*。鉴于 _mbs 系列函数不是一个完整的集合(例如,没有 _mbstol 将多字节字符串转换为长字符串),您被迫使用一些 char* 版本的运行时函数,由于这些函数之间的有符号/无符号类型差异,这会导致编译器问题。有人甚至使用这些吗?你只是做一大堆铸造来绕过错误吗?
  3. 在 C++ 中,std::string 具有迭代器,但这些迭代器基于 char_type,而不是基于代码点。因此,如果我在 std::string::iterator 上执行 ++,我会得到下一个 char_type,而不是下一个代码点。类似地,如果你调用 std::string::operator[],你会得到一个对 char_type 的引用,它很有可能不是一个完整的代码点。那么如何按代码点迭代 std::string 呢?(C 有 _mbsinc() 函数)。
4

3 回答 3

10

只做UTF-8

每个平台都有很多 UTF-8 支持库,也有一些是多平台的。正如您已经注意到的那样,Win32 中的 UTF-16 API 是有限且不一致的,因此最好将所有内容保留在 UTF-8 中并在最后一刻转换为 UTF-16。Windows API 也有一些方便的 UTF-8 包装。

此外,在应用程序级别的文档中,UTF-8 越来越被接受为标准。每个文本处理应用程序要么接受 UTF-8,要么在最坏的情况下将其显示为“带有一些 dingbats 的 ASCII”,而只有少数应用程序支持 UTF-16 文档,而那些不支持的应用程序则显示为“很多很多”的空白!”

于 2012-10-26T16:07:09.807 回答
8
  1. 正确的。您将为 Windows API 调用将 UTF-8 转换为 UTF-16。

  2. 大多数情况下,您将使用 UTF-8 的常规字符串函数 -- strlen, strcpy(ick), snprintf, strtol. 它们适用于 UTF-8 字符。要么char *用于 UTF-8,要么你必须转换所有内容。

    请注意,下划线版本 like_mbstowcs不是标准的,它们通常不带下划线命名,例如mbstowcs.

  3. 很难想出你真正想operator[]在 Unicode 字符串上使用的例子,我的建议是远离它。同样,遍历字符串的用途也很少:

    • 如果您正在解析一个字符串(例如,该字符串是 C 或 JavaScript 代码,也许您需要语法高亮),那么您可以逐字节完成大部分工作而忽略多字节方面。

    • 如果您正在搜索,您也将逐字节执行此操作(但请记住首先进行规范化)。

    • 如果您正在寻找断词或字素簇边界,您将需要使用像 ICU 这样的库。算法并不简单。

    • 最后,您始终可以将一大段文本转换为 UTF-32 并以这种方式使用它。我认为如果您要实现任何 Unicode 算法(如排序规则或中断),这是最明智的选择。

    请参阅:C++ 将 UTF-8 字符串迭代或拆分为符号数组?

于 2012-10-26T16:05:38.013 回答
2
  1. Windows 内部仅支持 UTF-16,因此如果您想支持国际字符,则必须转换为它们的 Widechar 版本以相应地使用操作系统调用。似乎不支持使用多字节 UTF-8 字符串调用 CreateFileA() 之类的东西,并让它看起来正确。这个对吗?

对,那是正确的。函数变体根据*A当前活动的代码页(在美国和西欧的大多数计算机上是 Windows-1252,但通常可以是其他代码页)解释字符串参数,并将它们转换为 UTF-16。有一个 UTF-8 代码页,但是 AFAIK 没有办法以编程方式设置活动代码页(GetACP可以获取活动代码页,但不对应SetACP)。

  1. 在 C 中,有一些支持多字节的函数(_mbscat、_mbscpy 等),但是在 Windows 上,这些函数的字符类型定义为 unsigned char*。鉴于 _mbs 系列函数不是一个完整的集合(例如,没有 _mbstol 将多字节字符串转换为长字符串),您被迫使用一些 char* 版本的运行时函数,由于这些函数之间的有符号/无符号类型差异,这会导致编译器问题。有人甚至使用这些吗?你只是做一大堆铸造来绕过错误吗?

根据mbs*我的经验,几乎从未使用过函数系列。除了mbstowcsmbsrtowcs和之外mbsinit,这些函数都不是标准 C。

  1. 在 C++ 中,std::string 具有迭代器,但这些迭代器基于 char_type,而不是基于代码点。因此,如果我在 std::string::iterator 上执行 ++,我会得到下一个 char_type,而不是下一个代码点。类似地,如果你调用 std::string::operator[],你会得到一个对 char_type 的引用,它很有可能不是一个完整的代码点。那么如何按代码点迭代 std::string 呢?(C 有 _mbsinc() 函数)。

我认为这mbrtowc(3)将是解码多字节字符串的单个代码点的最佳选择。

总的来说,我认为跨平台 Unicode 兼容性的最佳策略是在内部使用单字节字符在 UTF-8 中执行所有操作。当您需要调用 Windows API 函数时,将其转换为 UTF-16 并始终调用*W变体。大多数非 Windows 平台已经使用 UTF-8,因此使用起来很容易。

于 2012-10-26T16:04:49.607 回答