C++ 中的跨平台字符串(和 Unicode)

Posted

技术标签:

【中文标题】C++ 中的跨平台字符串(和 Unicode)【英文标题】:Cross-platform strings (and Unicode) in C++ 【发布时间】:2011-05-09 08:52:23 【问题描述】:

所以我终于回到了我的主要任务——将一个相当大的 C++ 项目从 Windows 移植到 Mac。

我马上就遇到了 wchar_t 在 Windows 上是 16 位但在 Mac 上是 32 位的问题。这是一个问题,因为所有字符串都由 wchar_t 表示,并且将有字符串数据在 Windows 和 Mac 机器之间来回传输(以磁盘数据和网络数据形式)。由于它的工作方式,在发送和接收数据之前将字符串转换为某种通用格式并不完全简单。

我们最近也真正开始支持更多的语言,因此我们开始处理大量 Unicode 数据(以及从右到左的语言)。

现在,我可能会在这里混淆多个想法,给自己带来比需要更多的问题,这就是我问这个问题的原因。我们认为将所有内存中的字符串数据存储为 UTF-8 是很有意义的。它解决了 wchar_t 大小不同的问题,这意味着我们可以轻松支持多种语言,并且还大大减少了我们的内存占用(我们加载了很多 - 主要是英语 - 字符串) - 但似乎没有很多人在做这。我们缺少什么吗?您必须处理一个明显的问题,即字符串长度可能小于存储该字符串数据的内存大小。

还是使用 UTF-16 更好?还是我们应该坚持使用 wchar_t 并编写代码在 wchar_t 和 Unicode 之间进行转换,在我们读/写磁盘或网络的地方?

我意识到这很危险地接近征求意见 - 但我们很担心我们忽略了一些明显的东西,因为它似乎没有很多 Unicode 字符串类(例如) - 但是有很多代码用于在 boost::locale、iconv、utf-cpp 和 ICU 中转换为 Unicode。

【问题讨论】:

只有一个字要说。 utf8everywhere.org 【参考方案1】:

当涉及文件或网络连接时,始终使用为字节定义的协议。不要依赖 C++ 编译器如何在内存中存储任何内容。对于 Unicode 文本,这意味着同时选择编码和字节顺序(好吧,UTF-8 不关心字节顺序)。即使您当前想要支持的平台具有相似的架构,也可能会出现另一个具有不同行为的流行平台,甚至是针对您现有平台之一的新操作系统,您会很高兴您编写了可移植的代码。

【讨论】:

【参考方案2】:

根据经验:UTF-16 用于处理,UTF-8 用于通信和存储。

当然,任何规则都可以被打破,而这条规则不是一成不变的。 但是你必须知道什么时候可以打破它。

例如,如果您使用的环境需要其他东西,那么使用其他东西可能是个好主意。但是 Mac OS X API 使用 UTF-16,与 Windows 相同。所以 UTF-16 更有意义。 在你把东西放到网上/得到东西之前进行转换(因为你可能在 2-3 个例程中完成)比进行所有转换来调用 OS API 更直接。

您开发的应用程序类型也很重要。 如果它的文本处理很少,对系统的调用也很少(比如电子邮件服务器,主要是在不改变它们的情况下移动东西),那么 UTF-8 可能是一个不错的选择。

因此,尽管您可能讨厌这个答案,但“这取决于”。

【讨论】:

【参考方案3】:

我倾向于使用 UTF-8 作为内部表示。您只会丢失字符串长度检查,无论如何都不是很有用。对于 Windows API 转换,我使用自己的 Win32 转换函数I devised here。正如 Mac 和 linux 一样(对于大部分标准 UTF-8-aware,不需要在那里转换任何东西)。您获得的免费奖金:

    使用普通的旧std::string。 逐字节网络/流传输。 对于大多数语言,内存占用良好。 更多功能:utf8cpp

【讨论】:

UTF-8 不允许允许你使用“plain old std::string”。也许如果您想要做的只是存储字符串就可以了,但是如果您使用该容器,如果不编写自己的 UTF-8 处理垃圾,您实际上无法以这种形式修改字符串。 (即你不能使用像std::string::find 这样的成员函数并期望它们能够正确处理UTF-8 字符串)太多人认为“哦,我将只使用UTF-8”并认为他们可以继续将所有内容都视为字符数组,这是错误的。 @Billy:对于任何多字节编码都是如此。 std::string 是一个字符容器,而不是字形,将 UTF-8 编码的文本保存在 std::string 中并用 utf8cpp 之类的东西处理它是非常好的 @Nemanja:是的,使用 std::string 进行存储很好,但从技术上讲,您可以在 std::string 中存储任何内容(只要您可以为它提供一个虚拟的std::char_traits 方面)。但是,当您说“您可以使用普通的旧 std::string”时,人们会假设他们实际上可以将该类用于数据存储以外的任何事情。如果只是存储是您所追求的,那么您可能应该改用vector @BillyONeal 事实上,您可以将string::find 与 UTF-8 一起使用,只要您使用它来查找特定的代码点序列而不是“等效”字符串(例如,组合与分解序列) @BillyONeal 是的,我知道可以做到这一点,并且对于 UTF-8 和任何其他 Unicode 编码一样容易做到。我不同意的是在 std::string 中使用 UTF-8 有一些特殊的缺点。您尚未描述可能解决此问题的首选替代方案,但您描述的缺点适用于 wchar_t*、std::wstring、char16/32_t*、std::u16/32string、C# 的字符串、MFC 的 CString、ICU 的 UnicodeString、NSString ,以及据我所知的几乎所有其他内容。【参考方案4】:

ICU 有一个 C++ 字符串类,UnicodeString

【讨论】:

ICU 是一个很好的库来处理这类东西。不幸的是,它也巨大(ICU 的编译大小约为 25MB)。在某些情况下这可能没问题,但在其他情况下(当然)就不行了。有些人实际上并不需要它提供的所有功能。 OTOH,任何自己实现它的人通常都会出错(诸如排序规则之类的事情因地区而异,而 ICU 会正确处理这些事情) 其中很多是 500 个语言环境和数百个转换器以及所有可能的库的数据。如果您不需要所有东西,从数据和代码的角度来看,它很容易定制。例如,核心 icuuc 库大约 1.4MB,不包括数据。

以上是关于C++ 中的跨平台字符串(和 Unicode)的主要内容,如果未能解决你的问题,请参考以下文章

C/C++ 中的跨平台 unicode:使用哪种编码?

跨平台 unicode 路径处理

Unicode字符串和非Unicode字符串

Unicode与编码方式

Unicode(统一码万国码单一码)

字符和字符串