libxml2
似乎将所有字符串存储为UTF-8,格式为xmlChar *
。
/**
* xmlChar:
*
* This is a basic byte in an UTF-8 encoded string.
* It's unsigned allowing to pinpoint case where char * are assigned
* to xmlChar * (possibly making serialization back impossible).
*/
typedef unsigned char xmlChar;
由于libxml2
是一个C库,因此没有提供例程来从std::wstring
中获取xmlChar *
。我想知道在C ++ 11中将xmlChar *
转换为std::wstring
的谨慎方法是否使用mbstowcs C函数,通过类似这样的方式(正在进行中):
std::wstring xmlCharToWideString(const xmlChar *xmlString) {
if(!xmlString){abort();} //provided string was null
int charLength = xmlStrlen(xmlString); //excludes null terminator
wchar_t *wideBuffer = new wchar_t[charLength];
size_t wcharLength = mbstowcs(wideBuffer, (const char *)xmlString, charLength);
if(wcharLength == (size_t)(-1)){abort();} //mbstowcs failed
std::wstring wideString(wideBuffer, wcharLength);
delete[] wideBuffer;
return wideString;
}
编辑:仅仅是一个FYI,我非常清楚xmlStrlen
返回的内容;它是用于存储字符串的xmlChar
的数量;我知道这不是字符的数量,而是unsigned char
的数量。如果我将它命名为byteLength
,那本来就不那么令人困惑了,但我认为它会更加清晰,因为我有charLength
和wcharLength
。至于代码的正确性,宽缓冲区将更大或相等到保持缓冲区所需的大小,总是(我相信)。因为需要比wide_t
更多空间的字符将被截断(我认为)。
答案 0 :(得分:5)
xmlStrlen()
返回xmlChar*
字符串中UTF-8编码的代码单元的数量。这与转换数据时所需的wchar_t
编码代码单元数量不会相同,因此请勿使用xmlStrlen()
来分配wchar_t
字符串的大小。您需要调用std::mbtowc()
一次以获得正确的长度,然后分配内存,并再次调用mbtowc()
以填充内存。您还必须使用std::setlocale()
告诉mbtowc()
使用UTF-8(弄乱语言环境可能不是一个好主意,特别是如果涉及多个线程)。例如:
std::wstring xmlCharToWideString(const xmlChar *xmlString)
{
if (!xmlString) { abort(); } //provided string was null
std::wstring wideString;
int charLength = xmlStrlen(xmlString);
if (charLength > 0)
{
char *origLocale = setlocale(LC_CTYPE, NULL);
setlocale(LC_CTYPE, "en_US.UTF-8");
size_t wcharLength = mbtowc(NULL, (const char*) xmlString, charLength); //excludes null terminator
if (wcharLength != (size_t)(-1))
{
wideString.resize(wcharLength);
mbtowc(&wideString[0], (const char*) xmlString, charLength);
}
setlocale(LC_CTYPE, origLocale);
if (wcharLength == (size_t)(-1)) { abort(); } //mbstowcs failed
}
return wideString;
}
一个更好的选择,因为你提到C ++ 11,是使用std::codecvt_utf8
代替std::wstring_convert
,所以你不必处理语言环境:
std::wstring xmlCharToWideString(const xmlChar *xmlString)
{
if (!xmlString) { abort(); } //provided string was null
try
{
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> conv;
return conv.from_bytes((const char*)xmlString);
}
catch(const std::range_error& e)
{
abort(); //wstring_convert failed
}
}
另一种选择是使用实际的Unicode库(如ICU或ICONV)来处理Unicode转换。
答案 1 :(得分:2)
此代码中存在一些问题,除了您使用wchar_t
和std::wstring
之外,这是一个坏主意,除非您正在调用Windows API。
xmlStrlen()
没有按照您的想法行事。它计算字符串中UTF-8代码单元(a.k.a.字节)的数量。它不计算字符数。这是documentation。
计算字符无论如何都无法为wchar_t
数组提供正确的大小。所以xmlStrlen()
不仅没有做你认为它做的事情,你想要的也不是正确的事情。问题是wchar_t
的编码因平台而异,使其对可移植代码100%无用。
mbtowcs()
函数与语言环境有关。如果语言环境是UTF-8语言环境,它只能转换为UTF-8!
如果std::wstring
构造函数抛出异常,此代码将泄漏内存。
我的建议:
如果可能的话,使用UTF-8。 wchar_t
兔子洞是没有好处的额外工作(除了能力之外)进行Windows API调用。)
如果您需要UTF-32,请使用std::u32string
。请记住,wstring
具有依赖于平台的编码:它可以是可变长度编码(Windows)或固定长度(Linux,OS X)。
如果你绝对必须拥有wchar_t
,那么你在Windows上的机会很大。以下是在Windows上执行此操作的方法:
std::wstring utf8_to_wstring(const char *utf8)
{
size_t utf8len = std::strlen(utf8);
int wclen = MultiByteToWideChar(
CP_UTF8, 0, utf8, utf8len, NULL, 0);
wchar_t *wc = NULL;
try {
wc = new wchar_t[wclen];
MultiByteToWideChar(
CP_UTF8, 0, utf8, utf8len, wc, wclen);
std::wstring wstr(wc, wclen);
delete[] wc;
wc = NULL;
return wstr;
} catch (std::exception &) {
if (wc)
delete[] wc;
}
}
如果您绝对必须拥有wchar_t
并且不在Windows上,请使用iconv()
(请参阅man 3 iconv
,man 3 iconv_open
和man 3 iconv_close
手册)。您可以将"WCHAR_T"
指定为iconv()
的编码之一。
请记住:您可能不希望wchar_t
或std::wstring
。 wchar_t
可移植无效,并使其有用不可移植。 C'est la vie。