跨平台c++:使用本地字符串编码或跨平台标准化
Cross-platform C++: Use the native string encoding or standardise across platforms?
我们特别关注Windows和Linux的开发,并提出了两种不同的方法,似乎都有各自的优点。Windows的自然unicode字符串类型是UTF-16, linux是UTF-8。
我们不能决定最好的方法:
-
在我们所有的应用程序逻辑(和持久数据)中对两者中的一个进行标准化,并使其他平台进行适当的转换
-
使用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提供了新的字符串类型u16string
和u32string
。根据编译器版本提供的支持和预期寿命,保持向前兼容可能是一个好主意。
除此之外,使用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)
或字节顺序的差异而导致的任何平台依赖性。