C ++ wchar_t和wstrings有什么“错误”? 什么是宽字符的替代品?

我见过很多C ++社区的人(特别是freenode上的## c ++),对于使用wstringswchar_t以及它们在windows api中的使用感到不满。 wchar_twstring究竟是什么“错误”,如果我想支持国际化,那么宽字符有什么替代?

什么是wchar_t?

wchar_t被定义为使得任何语言环境的字符编码都可以被转换成wchar_t表示,其中每个wchar_t只表示一个代码点:

typeswchar_t是一种不同的types,其值可以表示支持的语言环境(22.3.1)中指定的最大扩展字符集的所有成员的不同代码。

– C ++ [basic.fundamental] 3.9.1 / 5

并不要求wchar_t足够大,可以同时表示来自所有语言环境的任何字符。 也就是说,用于wchar_t的编码可能在区域设置上有所不同。 这意味着您不一定要使用一个语言环境将string转换为wchar_t,然后使用另一个语言环境转换回char。 1

由于使用wchar_t作为所有语言环境之间的通用表示法似乎是wchar_t在实践中的主要用途,所以您可能想知道如果不是这样的话,那么它是好的。

wchar_t的最初意图和目的是通过定义它,使得文本处理变得简单,使得它需要从string的代码单元到文本字符的一对一映射,从而允许使用与使用相同的简单algorithm用asciistring与其他语言一起工作。

不幸的是,wchar_t规范的措辞假定字符和代码点之间的一对一映射来实现这一点。 Unicode打破了假设2 ,所以你不能安全地使用wchar_t简单的文本algorithm。

这意味着便携式软件不能使用wchar_t作为区域之间文本的常见表示,也不能使用简单的文本algorithm。

wchar_t今天有什么用?

不多,对于便携式代码无论如何。 如果定义了__STDC_ISO_10646__则wchar_t的值直接表示在所有语言环境中具有相同值的Unicode代码点。 这样可以安全地执行前面提到的区域间转换。 但是你不能仅仅依靠它来决定你可以这样使用wchar_t,因为尽pipe大多数unix平台定义了它,但是Windows并没有在所有语言环境中使用相同的wchar_t语言环境。

Windows没有定义__STDC_ISO_10646__的原因是因为Windows使用UTF-16作为它的wchar_t编码,并且因为UTF-16使用代理对来表示大于U + FFFF的代码点,这意味着UTF-16不满足__STDC_ISO_10646__

对于特定于平台的代码,wchar_t可能更有用。 它基本上是Windows所需要的(例如,某些文件根本不能在不使用wchar_t文件名的情况下打开),尽pipe就我所知,Windows是唯一真实的平台(所以也许我们可以将wchar_t视为“Windows_char_t”)。

事后看来,wchar_t对于简化文本处理或者作为独立于语言环境的文本的存储显然没有用处。 便携式代码不应该试图用于这些目的。 不可移植的代码可能会因为某些API需要而发现它很有用。

备择scheme

我喜欢的select是使用UTF-8编码的Cstring,即使在对UTF-8不太友好的平台上。

通过这种方式,我们可以使用跨平台的通用文本表示来编写可移植的代码,使用标准数据types来达到预期的目的,获得语言对这些types的支持(例如,string文字,尽pipe一些技巧对于某些编译器来说是必要的)标准库支持,debugging器支持(可能需要更多的技巧)等等。对于宽字符,通常难以或不可能得到所有这些,并且可能在不同的平台上获得不同的部分。

UTF-8没有提供的一件事就是能够使用简单的文本algorithm,比如使用ASCII来实现。 在这个UTF-8没有任何其他的Unicode编码差。 实际上,它可能会被认为是更好的,因为UTF-8中的多代码单元表示更为常见,所以在代码处理中的错误比如果您试图使用UTF-8更容易注意和修复字符的可变宽度表示-32与NFC或NFKC。

许多平台使用UTF-8作为自己的本地字符编码,许多程序不需要任何重要的文本处理,因此在这些平台上编写国际化程序与编写代码时没有考虑国际化的区别。 编写更广泛的可移植代码或在其他平台上编写代码需要在使用其他编码的API的边界处插入转换。

某些软件使用的另一种方法是select一个跨平台的表示forms,比如保存UTF-16数据的无符号短数组,然后提供所有的库支持,并且只支持语言支持等方面的成本。

C ++ 11添加了新types的宽字符,作为wchar_t,char16_t和char32_t的替代品,以及附带的语言/库特性。 这些实际上并不保证是UTF-16和UTF-32,但我不认为任何主要的实现将使用其他任何东西。 C ++ 11还改进了对UTF-8的支持,例如使用UTF-8string文字,所以不必让VC ++生成UTF-8编码的string(尽pipe我可能会继续这样做,而不是使用u8前缀)。

避免的替代方法

TCHAR:TCHAR是用于迁移古代Windows程序的,假设遗留编码从char到wchar_t,最好忘记,除非你的程序是在以前的千年中编写的。 它不是可移植的,对于它的编码甚至是它的数据types本质上都是非特定的,使得它不能用于任何基于非TCHAR的API。 由于其目的是迁移到wchar_t,我们上面看到的不是一个好主意,所以使用TCHAR没有任何价值。


1.可以在wchar_tstring中表示但在任何语言环境中不支持的字符不需要用单个wchar_t值表示。 这意味着wchar_t可以对某些字符使用可变宽度编码,这又是对wchar_t意图的明显违反。 尽pipewchar_t表示的字符足以说明locale“支持”字符,在这种情况下,可变宽度编码是不合法的,并且Window对UTF-16的使用是不符合的。

2. Unicode允许用多个代码点来表示许多字符,这就为简单的文本algorithm创build了与可变宽度编码相同的问题。 即使严格维护一个合成规范化,有些字符仍然需要多个代码点。 请参阅: http : //www.unicode.org/standard/where/

wchar_t没有什么“错误”。 问题是,回到NT 3.x天,微软决定Unicode是好的,并将Unicode实现为16位wchar_t字符。 所以90年代中期的大多数微软文献都相当于Unicode == utf16 == wchar_t。

可悲的是,事实并非如此。 在所有情况下,“宽字符”不一定是2字节。

这是“Unicode”(独立于这个问题,独立于C ++)的最好的引擎之一,我见过:我强烈推荐它:

我真的相信,处理“8位ASCII”与“Win32宽字符”与“wchar_t-in-general”最好的办法就是接受“Windows是不同的”…并相应的编码。

恕我直言…

PS:

我完全同意上面的jamesdlin:

在Windows上,你并没有真正的select。 它的内部API是针对UCS-2而devise的,这是在变长UTF-8和UTF-16编码标准化之前,这是合理的。 但是现在,他们支持UTF-16,他们已经结束了与世界上最糟糕的。

强制性阅读:

The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)

如果你使用Java或.Net(VB.Net或C#)编程 – 这在很大程度上是一个非问题:默认情况下都是Unicode。 如果你在“经典的”Win32 API中编程),最好的办法是使用TCHAR和_T()macros(而不是明确使用wchar)。

所有的微软编译器VS2005和更高版本,我相信,无论如何,默认是C / C ++的16位(部分原因,我仍然使用MSVS 6.0);)。

另外一个好的(虽然有点过时的链接):