繁体   English   中英

libxml2 xmlChar *到std :: wstring

[英]libxml2 xmlChar * to std::wstring

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库,因此没有提供从xmlChar *获取std::wstring例程。 我想知道将xmlChar *转换为C ++ 11中的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 ,那本来就不那么令人困惑了,但我认为它会更加清晰,因为我有charLengthwcharLength 至于代码的正确性,宽缓冲区将大于或等于保持缓冲区所需的大小,总是(我相信)。 因为需要比wide_t更多空间的wide_t将被截断(我认为)。

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_utf8std::wstring_convert所以你不必处理locales:

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转换。

这段代码中存在一些问题,除了你使用wchar_tstd::wstring ,这是一个坏主意,除非你正在调用Windows API。

  1. xmlStrlen()不会按照您的想法执行操作。 它计算字符串中UTF-8代码单元(也称为字节)的数量。 它不计算字符数。 这是文档中的所有内容。

  2. 无论如何,计数字符都不会为wchar_t数组提供正确的大小。 所以xmlStrlen()不仅没有做你认为它做的事情,你想要的也不是正确的事情。 问题是wchar_t的编码因平台而异,使其对可移植代码100%无用。

  3. mbtowcs()函数依赖于语言环境。 如果语言环境是UTF-8语言环境,它只能转换为UTF-8!

  4. 如果std::wstring构造函数抛出异常,此代码将泄漏内存。

我的建议:

  1. 尽可能使用UTF-8。 wchar_t兔子洞是很多额外的工作, 没有任何好处(除了能够进行Windows API调用)。

  2. 如果你需要UTF-32,那么使用std::u32string 请记住, wstring具有依赖于平台的编码:它可以是可变长度编码(Windows)或固定长度(Linux,OS X)。

  3. 如果你绝对必须拥有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; } } 
  4. 如果你绝对必须有wchar_t并且你不在Windows上,请使用iconv() (参见man 3 iconvman 3 iconv_openman 3 iconv_close手册)。 您可以将"WCHAR_T"指定为iconv()的编码之一。

记住:你可能不想要wchar_tstd::wstring 什么wchar_t可以移植是没有用的,并使它有用是不可移植的。 这就是生活。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM