我一直在寻找一种在Unicode字符串类型之间转换并遇到this method的方法。我不仅不完全理解该方法(没有评论),而且该文章还暗示将来会出现更好的方法。
如果这是最好的方法,请您指出使它起作用的原因,否则,我想听听关于更好方法的建议。
最佳答案
mbstowcs()
和wcstombs()
不一定会转换为UTF-16或UTF-32,它们会转换为wchar_t
以及任何语言环境wchar_t
编码。所有Windows语言环境都使用2字节wchar_t
和UTF-16作为编码,但是其他主要平台使用4字节wchar_t
和UTF-32(对于某些语言环境甚至使用非Unicode编码)。仅支持单字节编码的平台甚至可以具有一个字节的wchar_t
,并且其编码因地区而异。因此,对于便携性和Unicode而言,wchar_t
似乎是一个糟糕的选择。 *
C++ 11中引入了一些更好的选项。 std::codecvt的新特化,新的codecvt类和新的模板使使用它们进行转换非常方便。
首先,使用编解码器的新模板类是std::wstring_convert。一旦创建了std::wstring_convert类的实例,就可以轻松地在字符串之间进行转换:
std::wstring_convert<...> convert; // ... filled in with a codecvt to do UTF-8 <-> UTF-16
std::string utf8_string = u8"This string has UTF-8 content";
std::u16string utf16_string = convert.from_bytes(utf8_string);
std::string another_utf8_string = convert.to_bytes(utf16_string);
为了进行不同的转换,您只需要不同的模板参数,其中之一是codecvt构面。以下是一些易于与wstring_convert一起使用的新方面:
std::codecvt_utf8_utf16<char16_t> // converts between UTF-8 <-> UTF-16
std::codecvt_utf8<char32_t> // converts between UTF-8 <-> UTF-32
std::codecvt_utf8<char16_t> // converts between UTF-8 <-> UCS-2 (warning, not UTF-16! Don't bother using this one)
使用这些示例:
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> convert;
std::string a = convert.to_bytes(u"This string has UTF-16 content");
std::u16string b = convert.from_bytes(u8"blah blah blah");
新的std::codecvt特化名称很难使用,因为它们具有 protected 析构函数。为了解决这个问题,您可以定义一个具有析构函数的子类,或者可以使用std::use_facet模板函数来获取现有的编解码器实例。此外,这些特化的问题是您不能在Visual Studio 2010中使用它们,因为模板特化不适用于typedef类型,并且编译器将char16_t和char32_t定义为typedef。这是定义自己的codecvt子类的示例:
template <class internT, class externT, class stateT>
struct codecvt : std::codecvt<internT,externT,stateT>
{ ~codecvt(){} };
std::wstring_convert<codecvt<char16_t,char,std::mbstate_t>,char16_t> convert16;
std::wstring_convert<codecvt<char32_t,char,std::mbstate_t>,char32_t> convert32;
char16_t特化在UTF-16和UTF-8之间转换。 char32_t专长是UTF-32和UTF-8。
请注意,C++ 11提供的这些新转换不包含任何在UTF-32和UTF-16之间直接转换的方法。相反,您只需要组合std::wstring_convert的两个实例。
*****我想我会在wchar_t及其目的上添加一条注释,以强调为什么通常不应该将它用于Unicode或可移植的国际化代码。以下是我的答案https://stackoverflow.com/a/11107667/365496的简短版本
什么是wchar_t?
wchar_t的定义使得任何语言环境的char编码都可以转换为wchar_t,其中每个wchar_t都恰好表示一个代码点:
这不需要wchar_t足够大以同时表示来自所有语言环境的任何字符。即,用于wchar_t的编码在语言环境之间可能有所不同。这意味着您不一定必须使用一种语言环境将字符串转换为wchar_t,然后使用另一种语言环境转换回char。
由于这似乎是wchar_t在实践中的主要用途,因此您可能会想知道这样做的好处。
wchar_t的最初意图和目的是通过定义文本来简化文本处理,从而要求从字符串的代码单元到文本字符的一对一映射,从而允许使用与ascii字符串相同的简单算法与其他语言一起使用。
不幸的是,对wchar_t的要求假设字符和代码点之间是一对一的映射才能实现。 Unicode打破了这一假设,因此您也无法安全地将wchar_t用于简单的文本算法。
这意味着便携式软件不能将wchar_t用作语言环境之间文本的通用表示形式,也不能使用简单的文本算法。
今天的wchar_t有什么用?
不管怎样,对于可移植的代码而言。如果定义了
__STDC_ISO_10646__
,则wchar_t的值将直接表示在所有语言环境中具有相同值的Unicode代码点。这样可以安全地进行前面提到的区域间转换。但是,您不能仅依靠它来决定可以以这种方式使用wchar_t,因为尽管大多数Unix平台都定义了它,但是Windows并没有,即使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文件名就无法打开),尽管据我所知Windows是唯一的真实平台(所以也许我们可以将wchar_t视为'Windows_char_t')。
在事后看来,wchar_t对于简化文本处理或存储独立于区域设置的文本显然没有用。可移植代码不应尝试将其用于这些目的。