在string,u16string和u32string之间转换

我一直在寻找一种在Unicodestringtypes之间进行转换的方法,并且遇到了这种方法 。 我不仅没有完全理解这个方法(没有评论),而且文章暗示将来会有更好的方法。

如果这是最好的方法,请指出是什么使其工作,如果不是,我希望听到更好的方法的build议。

mbstowcs()wcstombs()不一定会转换为UTF-16或UTF-32,它们会转换为wchar_t ,无论wchar_t编码的语言环境如何。 所有Windows语言环境都使用两个字节的wchar_t和UTF-16作为编码,但其他主要平台使用UTF-32的4个字节的wchar_t (甚至对某些语言环境甚至使用非Unicode编码)。 仅支持单字节编码的平台甚至可以具有一个字节的wchar_t并且编码因地区而异。 所以wchar_t在我看来对于可移植性和Unicode来说是一个糟糕的select。 *

在C ++ 11中引入了一些更好的选项; std :: codecvt的新特化,新的codecvt类,以及一个新的模板,使转换非常方便。

首先,使用codecvt的新模板类是std :: wstring_convert。 一旦你创build了一个std :: wstring_convert类的实例,你可以很容易地在string之间进行转换:

 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专业化有点难以使用,因为他们有一个受保护的析构函数。 为了解决这个问题,你可以定义一个具有析构函数的子类,或者你可以使用std :: use_facet模板函数来获得一个现有的codecvt实例。 此外,这些专业化的问题是你不能在Visual Studio 2010中使用它们,因为模板专门化不适用于typedef'dtypes,编译器将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是这样定义的,任何locale的char编码都可以转换成wchar_t,其中每个wchar_t只代表一个codepoint:

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

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

因为这似乎是wchar_t在实践中的主要用途,所以如果不是这样的话,您可能会想知道它有什么好处。

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

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

这意味着便携式软件不能使用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对于简化文本处理或者作为独立于语言环境的文本的存储显然没有用处。 便携式代码不应该试图用于这些目的。

我写了帮助函数来转换为UTF8string(C ++ 11):

 #include <string> #include <locale> #include <codecvt> using namespace std; template <typename T> string toUTF8(const basic_string<T, char_traits<T>, allocator<T>>& source) { string result; wstring_convert<codecvt_utf8_utf16<T>, T> convertor; result = convertor.to_bytes(source); return result; } template <typename T> void fromUTF8(const string& source, basic_string<T, char_traits<T>, allocator<T>>& result) { wstring_convert<codecvt_utf8_utf16<T>, T> convertor; result = convertor.from_bytes(source); } 

用法示例:

 // Unicode <-> UTF8 { wstring uStr = L"Unicode string"; string str = toUTF8(uStr); wstring after; fromUTF8(str, after); assert(uStr == after); } // UTF16 <-> UTF8 { u16string uStr; uStr.push_back('A'); string str = toUTF8(uStr); u16string after; fromUTF8(str, after); assert(uStr == after); } 

据我所知,C ++没有提供从UTF-32转换为UTF-32的标准方法。 但是,对于UTF-16,有mbstowcs (多字节到宽string)和反向wcstombs方法

如果你也需要UTF-32,你需要iconv ,这是在POSIX 2001中,但不是在标准的C,所以在Windows上,你需要像libiconv替代。

这里是一个关于如何使用mbstowcs的例子:

 #include <string> #include <iostream> #include <stdlib.h> using namespace std; wstring widestring(const string &text); int main() { string text; cout << "Enter something: "; cin >> text; wcout << L"You entered " << widestring(text) << ".\n"; return 0; } wstring widestring(const string &text) { wstring result; result.resize(text.length()); mbstowcs(&result[0], &text[0], text.length()); return result; } 

反过来是这样的:

 string mbstring(const wstring &text) { string result; result.resize(text.length()); wcstombs(&result[0], &text[0], text.length()); return result; } 

Nitpick:是的,我知道,wchar_t的大小是实现定义的,所以它可以是4字节(UTF-32)。 但是,我不知道这样做的编译器。