我一直在寻找一种方法来转换不同的Unicode字符串类型,并发现了这个方法。我不仅完全不理解这个方法(没有注释),而且文章暗示未来会有更好的方法。
如果这是最好的方法,请指出它的优点,如果不是,我想听听更好的建议。
mbstowcs()
和wcstombs()
并不一定转换为UTF-16或UTF-32,它们会转换为wchar_t
以及本地wchar_t
编码。所有Windows区域设置都使用了一个两字节的wchar_t
和UTF-16作为编码,但其他主要平台使用了一个4字节的wchar_t
与UTF-32(甚至某些区域设置使用非Unicode编码)。一个只支持单字节编码的平台甚至可以有一个一字节的wchar_t
,并且编码会因区域设置而异。因此,对于可移植性和Unicode来说,wchar_t
似乎是一个糟糕的选择。*
C++11引入了一些更好的选项:新的std::codecvt特化、新的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);
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");
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://dev59.com/02gu5IYBdhLWcg3wv5ca#11107667
wchar_t被定义为可以将任何语言环境的字符编码转换为wchar_t,其中每个wchar_t表示一个代码点:
类型wchar_t是一个独特的类型,其值可以表示所支持语言环境中最大扩展字符集的所有成员的不同代码。 -- [basic.fundamental] 3.9.1/5
这并不要求wchar_t足够大以同时表示来自所有语言环境的任何字符。也就是说,wchar_t的编码可能因语言环境而异。这意味着您不能使用一个语言环境将字符串转换为wchar_t,然后使用另一个语言环境将其转换回char。
由于这似乎是wchar_t在实践中的主要用途,因此您可能会想知道它的用途是什么。
wchar_t的最初意图和目的是通过定义它,使文本处理变得简单,使其需要从字符串的代码单元到文本的字符的一对一映射,从而允许使用与ascii字符串相同的简单算法来处理其他语言。
不幸的是,wchar_t的要求假定字符和代码点之间存在一对一的映射以实现这一点。Unicode打破了这个假设,因此即使对于简单的文本算法,您也不能安全地使用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显然不适用于简化文本处理,也不适用于存储与语言环境无关的文本。可移植的代码不应尝试将其用于这些目的。
我已经编写了一些助手函数来转换 UTF8 字符串(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);
}
#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;
}
小挑剔:是的,我知道,wchar_t的大小是由实现定义的,所以它可能是4个字节(UTF-32)。然而,我不知道有哪个编译器会这样做。
wchar_t
。 - dan04