跨平台c++:使用本地字符串编码或跨平台标准化

Cross-platform C++: Use the native string encoding or standardise across platforms?

本文关键字:跨平台 字符串 编码 标准化 c++      更新时间:2023-10-16

我们特别关注Windows和Linux的开发,并提出了两种不同的方法,似乎都有各自的优点。Windows的自然unicode字符串类型是UTF-16, linux是UTF-8。

我们不能决定最好的方法:

  1. 在我们所有的应用程序逻辑(和持久数据)中对两者中的一个进行标准化,并使其他平台进行适当的转换

  2. 使用OS的自然格式用于应用程序逻辑(从而调用OS),并且仅在IPC和持久性点进行转换。

对我来说,他们似乎都一样好。

和linux中的UTF-8。

对于现代Linux来说基本是这样。实际上编码取决于所使用的API或库。一些硬编码使用UTF-8。但有些读取LC_ALL, LC_CTYPE或LANG环境变量来检测要使用的编码(如Qt库)。所以要小心。

我们无法决定是否最好的方法

还是看情况而定。

如果90%的代码是用特定于平台的方式处理特定于平台的API,显然使用特定于平台的字符串会更好。作为一个例子——一个设备驱动程序或原生iOS应用程序。

如果90%的代码是跨平台共享的复杂业务逻辑,显然最好在所有平台上使用相同的编码。作为一个例子-聊天客户端或浏览器。

在第二种情况下,你有一个选择:

  • 使用提供字符串支持的跨平台库(例如Qt, ICU)
  • 使用裸指针(我认为std::string也是一个"裸指针")

如果处理字符串是应用程序的重要组成部分,那么为字符串选择一个好的库是明智之举。例如,Qt有一个非常可靠的类集,涵盖了99%的常见任务。不幸的是,我没有ICU的经验,但它看起来也很好。

当使用一些库来处理字符串时,你只需要在使用外部库、平台API或通过网络(或磁盘)发送字符串时才需要关心编码。例如,许多Cocoa, c#或Qt(都有坚实的字符串支持)程序员对编码细节知之甚少(这很好,因为他们可以专注于他们的主要任务)。

我在处理字符串方面的经验是有点特定的,所以我个人更喜欢裸指针。使用它们的代码非常可移植(从某种意义上说,它可以很容易地在其他项目和平台中重用),因为对外部依赖较少。它也非常简单和快速(但可能需要一些经验和Unicode背景才能感受到这一点)。

我同意裸指针方法并不适合所有人。

  • 您使用整个字符串和分割,搜索,比较是一个罕见的任务
  • 可以在所有组件中使用相同的编码,只有在使用平台API时才需要转换
  • 所有支持的平台都有API:
    • 将您的编码转换为API中使用的编码
    • 将API编码转换为代码中使用的编码
  • 指针在你的团队中不是问题

从我的一点具体经验来看,这其实是一个很常见的情况。

使用裸指针时,最好选择在整个项目(或所有项目)中使用的编码。

在我看来,UTF-8是最终的赢家。如果你不能使用UTF-8 -使用字符串库或平台API的字符串-这将节省你很多时间。

UTF-8的优点:

  • 完全ASCII兼容。任何ASCII字符串都是有效的UTF-8字符串。
  • C std库可以很好地处理UTF-8字符串。(*)
  • c++ std库与UTF-8 (std::string等)配合得很好。(*)
  • 遗留代码在UTF-8下工作得很好。
  • 几乎所有的平台都支持UTF-8。
  • 使用UTF-8调试更容易(因为它是ASCII兼容的)。
  • 没有小端/大端混乱。
  • 你不会捕获一个经典的错误"哦,UTF-16不总是2字节?"。

(*)除非你需要对它们进行词法比较,转换大小写(toUpper/toLower),改变规范化形式或类似的东西-如果你需要的话-使用字符串库或平台API。

缺点值得怀疑:

  • 与UTF-16相比,中文(和其他代码点数较大的符号)更不紧凑。
  • 更难(实际上有点)迭代符号。

因此,我建议使用UTF-8作为不使用任何字符串库的项目的通用编码。

但是编码并不是你需要回答的唯一问题。

存在标准化。简单地说,有些字母可以用几种方式表示——比如一个字形或不同字形的组合。这样做的常见问题是,大多数字符串比较函数将它们视为不同的符号。如果您从事的是跨平台项目,那么选择其中一种规范化形式作为标准是正确的。这会节省你的时间。

例如,如果用户密码包含"йёжиг",当在Mac(主要使用规范化表单D)和Windows(主要喜欢规范化表单C)上输入时,它将以不同的方式表示(在UTF-8和UTF-16中)。因此,如果用户在Windows下注册这样的密码,这将是他在Mac下登录的问题。

另外,我不建议使用wchar_t(或者只在windows代码中使用它作为UCS-2/UTF-16字符类型)。wchar_t的问题在于没有与之相关的编码。它只是一个抽象的宽字符,比普通字符大(Windows为16位,大多数*nix为32位)。

我将在内部使用相同的编码,并在入口点规范化数据。这将涉及更少的代码,更少的陷阱,并允许您使用相同的跨平台库进行字符串处理。

我将使用unicode (utf-16),因为它更容易在内部处理,并且由于每个字符的长度不变,应该执行得更好。UTF-8对于输出和存储是理想的,因为它向后兼容拉丁ascii,并且只使用8位的英文字符。但在程序内部,16位更容易处理。

c++ 11提供了新的字符串类型u16stringu32string。根据编译器版本提供的支持和预期寿命,保持向前兼容可能是一个好主意。

除此之外,使用ICU库可能是实现跨平台兼容性的最佳方法。

这似乎对这个话题很有启发。http://www.utf8everywhere.org/

使用UTF-8编程很困难,因为长度和偏移量混淆了。例如

std::string s = Something();
std::cout << s.substr(0, 4);

不一定能找到前4个字符。

我将使用wchar_t是什么。在Windows上是UTF-16。在某些*nix平台上,它可能是UTF-32。

保存到文件时,我建议转换为UTF-8。这通常会使文件更小,并消除由于sizeof(wchar_t)或字节顺序的差异而导致的任何平台依赖性。