跨平台的C++:使用本地字符串编码还是在各个平台上统一?

10
我们专注于Windows和Linux开发,并提出了两种不同的方法,它们似乎都有各自的优点。在Windows中自然的Unicode字符串类型是UTF-16,在Linux中则是UTF-8。
我们无法决定最佳方法:
1. 在所有应用程序逻辑(和持久化数据)中标准化其中一种格式,并使其他平台执行相应的转换。 2. 使用操作系统的自然格式进行应用程序逻辑(从而调用操作系统),并仅在IPC和持久性点进行转换。
对我来说,它们看起来非常相似,都很好。

2
我们应该选择什么,为什么?不明显吗? - Jesse Pepper
什么,而不是什么的。该死的手机。 - Jesse Pepper
1
一些跨平台库(例如Qt)可能会做出自己的选择。如果您使用它们,请遵循它们的想法(例如在使用Qt时使用QString和QChar)。 - Basile Starynkevitch
@Basile - 如果你想在不同平台上使用的第三方库彼此之间存在冲突怎么办?例如,在Windows上使用Win32控件,在Linux上使用GTK+? - Daniel Paull
5个回答

6
在现代Linux系统上,大多数情况下是使用UTF-8编码的。实际上,编码取决于使用的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标准库与UTF-8字符串完美配合。(*)
  • C++标准库与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的问题是它没有与之相关联的编码。它只是一个比普通char更大的抽象宽字符(在Windows上为16位,在大多数*nix上为32位)。


0

0

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

我会使用Unicode(UTF-16),因为它在内部处理起来更简单,并且由于每个字符具有恒定的长度,因此应该具有更好的性能。UTF-8非常适合输出和存储,因为它向后兼容拉丁ASCII,并且仅对英文字符使用8位。但在程序内部,16位更简单易用。


2
UTF-16 每个字符的长度不是固定的,它是一种可变长度编码。也许你想到的是 UTF-32,这是唯一标准的固定长度 Unicode 编码。 - David Heffernan
1
而我今天学到的是,抽象Unicode字符实际上可能由多个代码点组成,因此如果你处理抽象Unicode字符,那么UTF-32仍然是可变长度的! - Daniel Paull
@DavidHeffernan: 他可能在想UCS2,这是Microsoft的wchar_t基础上的16位编码。;-) - DevSolar
@DevSolar 我认为你理解错了。UTF-16使用16位字符点,只是有些代码点需要多个字符点。它是一种可变长度编码。就像UTF-8使用8位字符单元一样。 - David Heffernan
@DavidHeffernan:如果你一个16位的wchar_t。由于MS和Linux在其宽度上存在分歧,这使得它对于跨平台编码几乎没有用处,不是吗? - DevSolar
显示剩余5条评论

0

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

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


-1

使用UTF-8进行编程很困难,因为长度和偏移量混淆在一起。例如:

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

不一定找到前4个字符。

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

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


2
UTF-16 也有同样的问题。只是在西方字符中你可能不会注意到。 - Gort the Robot
这是真的,但Windows不支持UTF-32。大多数UTF-16字符> 0xFFFF都是历史字符集,例如古希腊文和楔形文字,但也有一些可能很重要,例如HAN CJK扩展。开发人员需要决定是否重要。如果是,则解决方案变得更加复杂。 - Michael J
真的。我忘了汉字只需要16位就可以存储。 - Gort the Robot
能否解释一下给这个点赞踩的人,出了什么问题吗?谢谢。 - Michael J

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接