libxml2 xmlChar *到std :: wstring

时间:2013-01-01 01:28:50

标签: c++ string unicode libxml2

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,那本来就不那么令人困惑了,但我认为它会更加清晰,因为我有charLengthwcharLength。至于代码的正确性,宽缓冲区将更大或相等到保持缓冲区所需的大小,总是(我相信)。因为需要比wide_t更多空间的字符将被截断(我认为)。

2 个答案:

答案 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_tstd::wstring之外,这是一个坏主意,除非您正在调用Windows API。

  1. xmlStrlen()没有按照您的想法行事。它计算字符串中UTF-8代码单元(a.k.a.字节)的数量。它不计算字符数。这是documentation

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

  4. mbtowcs()函数与语言环境有关。如果语言环境是UTF-8语言环境,它只能转换为UTF-8!

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

  6. 我的建议:

    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()的编码之一。

    5. 请记住:您可能不希望wchar_tstd::wstringwchar_t可移植无效,并使其有用不可移植。 C'est la vie。