UTF8与STL中的宽字符转换
是否可以以平台无关的方式将std :: string中的UTF8字符串转换为std :: wstring,反之亦然?在Windows应用程序中,我将使用MultiByteToWideChar和WideCharToMultiByte。但是,代码是为多个操作系统编译的,我仅限于标准C ++库。
解决方案
我们可以使用codecvt
语言环境方面。有一个特定的特殊定义," codecvt <wchar_t,char,mbstate_t>"可能对我们有用,尽管这种行为是系统特定的,并且不保证以任何方式转换为UTF-8.
我认为没有做到这一点的便携式方法。 C ++不知道其多字节字符的编码。
正如克里斯建议的那样,我们最好的选择是使用编解码器。
我们可以从Boost序列化库中提取utf8_codecvt_facet
。
其用法示例:
typedef wchar_t ucs4_t; std::locale old_locale; std::locale utf8_locale(old_locale,new utf8_codecvt_facet<ucs4_t>); // Set a New global locale std::locale::global(utf8_locale); // Send the UCS-4 data out, converting to UTF-8 { std::wofstream ofs("data.ucd"); ofs.imbue(utf8_locale); std::copy(ucs4_data.begin(),ucs4_data.end(), std::ostream_iterator<ucs4_t,ucs4_t>(ofs)); } // Read the UTF-8 data back in, converting to UCS-4 on the way in std::vector<ucs4_t> from_file; { std::wifstream ifs("data.ucd"); ifs.imbue(utf8_locale); ucs4_t item = 0; while (ifs >> item) from_file.push_back(item); }
在增强源中查找" utf8_codecvt_facet.hpp"和" utf8_codecvt_facet.cpp"文件。
有几种方法可以做到这一点,但是结果取决于string
和wstring
变量中字符编码的含义。
如果我们知道string
是ASCII,则可以简单地使用wstring
的迭代器构造函数:
string s = "This is surely ASCII."; wstring w(s.begin(), s.end());
但是,如果string
具有其他编码,我们将得到非常糟糕的结果。如果编码是Unicode,则可以看一下ICU项目,该项目提供了一套跨平台的库,这些库可以在各种Unicode编码之间进行转换。
如果`string'在代码页中包含字符,则$ DEITY可能会发怒。
问题定义明确指出8位字符编码为UTF-8. 这使这成为一个琐碎的问题;从一个UTF规范转换到另一个UTF规范,所需要做的只是花点时间。
只需查看这些Wikipedia页面上的UTF-8,UTF-16和UTF-32编码即可。
原理很简单,根据一个UTF规范通过输入并组装一个32位Unicode代码点,然后根据另一个规范发出代码点。各个代码点不需要翻译,就象任何其他字符编码一样。这就是使这成为一个简单问题的原因。
这是将wchar_t转换为UTF-8的快速实现,反之亦然。假定输入已经正确编码,这就是俗语"垃圾进,垃圾出"在这里适用的情况。我认为最好将编码验证作为一个单独的步骤来完成。
std::string wchar_to_UTF8(const wchar_t * in) { std::string out; unsigned int codepoint = 0; for (in; *in != 0; ++in) { if (*in >= 0xd800 && *in <= 0xdbff) codepoint = ((*in - 0xd800) << 10) + 0x10000; else { if (*in >= 0xdc00 && *in <= 0xdfff) codepoint |= *in - 0xdc00; else codepoint = *in; if (codepoint <= 0x7f) out.append(1, static_cast<char>(codepoint)); else if (codepoint <= 0x7ff) { out.append(1, static_cast<char>(0xc0 | ((codepoint >> 6) & 0x1f))); out.append(1, static_cast<char>(0x80 | (codepoint & 0x3f))); } else if (codepoint <= 0xffff) { out.append(1, static_cast<char>(0xe0 | ((codepoint >> 12) & 0x0f))); out.append(1, static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f))); out.append(1, static_cast<char>(0x80 | (codepoint & 0x3f))); } else { out.append(1, static_cast<char>(0xf0 | ((codepoint >> 18) & 0x07))); out.append(1, static_cast<char>(0x80 | ((codepoint >> 12) & 0x3f))); out.append(1, static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f))); out.append(1, static_cast<char>(0x80 | (codepoint & 0x3f))); } codepoint = 0; } } return out; }
上面的代码适用于UTF-16和UTF-32输入,仅因为范围从d800到dfff是无效的代码点。它们表示我们正在解码UTF-16. 如果我们知道wchar_t是32位,则可以删除一些代码以优化该功能。
std::wstring UTF8_to_wchar(const char * in) { std::wstring out; unsigned int codepoint; while (*in != 0) { unsigned char ch = static_cast<unsigned char>(*in); if (ch <= 0x7f) codepoint = ch; else if (ch <= 0xbf) codepoint = (codepoint << 6) | (ch & 0x3f); else if (ch <= 0xdf) codepoint = ch & 0x1f; else if (ch <= 0xef) codepoint = ch & 0x0f; else codepoint = ch & 0x07; ++in; if (((*in & 0xc0) != 0x80) && (codepoint <= 0x10ffff)) { if (sizeof(wchar_t) > 2) out.append(1, static_cast<wchar_t>(codepoint)); else if (codepoint > 0xffff) { out.append(1, static_cast<wchar_t>(0xd800 + (codepoint >> 10))); out.append(1, static_cast<wchar_t>(0xdc00 + (codepoint & 0x03ff))); } else if (codepoint < 0xd800 || codepoint >= 0xe000) out.append(1, static_cast<wchar_t>(codepoint)); } } return out; }
同样,如果我们知道wchar_t是32位,则可以从该函数中删除一些代码,但是在这种情况下,它不会有任何区别。表达式" sizeof(wchar_t)> 2"在编译时是已知的,因此任何合适的编译器都将识别无效代码并将其删除。
UTF8-CPP:以可移植方式使用C ++的UTF-8
ConvertUTF.h
ConvertUTF.c
感谢bames53提供更新版本