如何在C/C++中处理Unicode字符序列?

11

有哪些更加轻便和干净的方式来处理C和C++中的Unicode字符序列?

此外,如何:

- 读取Unicode字符串

- 将Unicode字符串转换为ASCII以节省一些字节(如果用户仅输入ASCII)

- 打印Unicode字符串

我应该使用环境吗?例如,我已经阅读了LC_CTYPE,作为开发人员,我应该关心它吗?


与https://dev59.com/1VDTa4cB1Zd3GeqPGyqi相关的内容。 - dan04
4个回答

8
“在C和C++中处理unicode字符序列的更便携和清晰的方法是什么?”
“在您的程序中,将所有字符串设置为{{link1:UTF-8,UTF-16或UTF-32}}。如果由于某种原因需要处理非Unicode编码,则进行输入和输出转换。”
“读取Unicode字符串的方法与读取ASCII文件的方式相同。但是仍然存在大量非Unicode数据,因此您需要检查数据是否为Unicode。如果不是(或者如果它是UTF-8而您首选的内部编码是UTF-32),则需要进行转换。”
“UTF-8和UTF-32可以通过验证可靠地检测到。”
“UTF-16可以通过BOM的存在来检测。”
“如果不是UTF编码,则可能是ISO-8859-1或windows-1252编码。”
将Unicode字符串转换为ASCII以节省一些字节(如果用户只输入ASCII)。
不要这样做。如果您的数据全部是ASCII,则UTF-8将占用完全相同的空间。如果它不是,则在转换为ASCII时会丢失信息。如果您关心节省字节,请执行以下操作:
1.选择最佳的UTF编码。对于U+0000到U+007F之间的字符,UTF-8是最小的。对于U+0800到U+FFFF之间的字符,UTF-16是最小的。
2.使用像gzip这样的数据压缩。有一个专门为Unicode设计的SCSU编码,但我不知道它有多好。
打印Unicode字符串。
写入UTF-8与写入ASCII没有区别。
除了在Windows命令提示符下,因为它仍然使用旧的“OEM”代码页。在那里,您可以使用WriteConsoleW与UTF-16字符串。

我也应该使用环境吗? 我已经了解了 LC_CTYPE,作为开发人员,我需要关注它吗?

LC_CTYPE 是过去每种语言都有自己的字符编码和 ctype.h 函数时代的遗留物。今天,Unicode Character Database 负责处理这个问题。Unicode 的美妙之处在于它将字符编码处理与区域设置处理分开(除了立陶宛语、土耳其语和阿塞拜疆语的 特殊大写/小写规则)。

但是,每种语言仍然有自己的排序规则和数字格式规则,因此您仍然需要区域设置来处理这些内容。并且您需要将您的区域设置的字符编码设置为 UTF-8。


1
非常好的概述,特别是因为它避免了任何编程语言特定的内容。 - Jens Gustedt

3
在C和C++中处理Unicode字符序列的更便携和清洁的方法是什么?使用类似ICU的库。如果你不能,那就绝对不能自己实现。尽管如此,还是要查看Unicode.or的示例源代码文档。
是否应该使用环境?是的。你可能需要使用std::setlocale函数。这将允许你设置与所需编码相对应的区域设置,例如,如果你想使用英式英语作为语言和UTF-8作为编码,则需要将LC_CTYPE设置为en_GB.UTF8
C++03没有提供处理Unicode的方法。最好的方法是使用数据类型(以及扩展的)。然而,请注意,不同操作系统上的大小和字符编码是不同的。例如,Windows使用2个字节来表示和UTF-16编码,而GNU/Linux和Mac OSX使用4个字节和UTF-32。
C++0x应该通过允许Unicode文字面量、codecvt facets、C Unicode TR支持(读取)等来修正这种情况,但对于大多数编译器来说,这还有很长的路要走。(在SO上有一些问题可以帮助您入门。)

1
-1 std::wstring != The Unicode String; std::string是完全可以表示Unicode字符串的,就像std::wstring一样! - Artyom
我的观点是:std::wstring 对于 UTF16(在 Windows 上)和 UTF32(在 Mac/Linux 上)可以很有用。UTF8 的最大问题在于它是一种可变宽度编码,因此 charwchar_t 可能无法跨平台表示 Unicode 字符。 - dirkgently
1
UTF-16也是可变宽度编码。即使只有一个代码点的访问权限通常也是无用的,因为它甚至不能表示一个字符。因此,对于文本分析,您需要使用强大的库,如ICU;而对于基本用途,std::string与UTF-8一样完美,不需要使用宽字符串。 - Artyom
关于UTF-8,是的,两种方式都可以。如果你注意到了,我的回答的第一行提到了ICU。 - dirkgently

0

如果需要读取、打印或将Unicode转换为ASCII,只需使用UTF-8即可,这对您来说完全透明。

  • 读取、写入没有区别
  • ASCII已经是UTF-8的子集

对于文本分析/处理,请使用像ICU、Boost.Locale或甚至Qt、Glib这样的优秀库,它们提供了相当好的文本分析/处理工具。


0

在这篇回答之前,有很多好的答案,但是没有一个提到我认为可能存在的问题,因为这个问题也有 C 标签。我的 C 知识已经过时了,如果我错了,请纠正我。

请注意,传统的 C 字符串函数和 UTF-16 编码数据流以及零终止字符串可能是一个棘手的组合,因为在 UTF-16 中,许多西方字母数字字符将被编码为两个字节,其中另一个字节都是零,因此将字符数据读取为一系列的 char 不再像单字节字符集那样简单。


您可以使用以0x0000结尾的UTF-16字符串。如上所述,ICU对此提供了广泛的支持。正如您所指出的那样,您不能假设UTF-16适合8位字符。 - Steven R. Loomis

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