【问题标题】:libxml2 xmlChar * to std::wstringlibxml2 xmlChar * 到 std::wstring
【发布时间】:2013-01-01 01:28:50
【问题描述】:

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 的例程。我想知道在 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;
}

编辑:仅供参考,我非常清楚xmlStrlen 返回的内容;它是用于存储字符串的xmlChar 的编号;我知道这不是 characters 的数量,而是unsigned char 的数量。如果我将它命名为byteLength,它会不会那么混乱,但我认为它会更清晰,因为我同时拥有charLengthwcharLength。至于代码的正确性,wideBuffer 将始终大于或等于 以保持缓冲区所需的大小(我相信)。因为比wide_t 需要更多空间的字符将被截断(我认为)。

【问题讨论】:

  • 如果您想谈论最谨慎的做法,请避免同时使用wchar_twstring。使用 Unicode 时,它​​们弊大于利。

标签: c++ string unicode libxml2


【解决方案1】:

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,这样您就不必处理语言环境:

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

【讨论】:

  • std::codecvt_utf8std::wstring_convert 看起来是一个很好的选择。谢谢!
【解决方案2】:

除了您使用wchar_tstd::wstring 之外,此代码中存在一些问题,除非您正在调用 Windows API,否则这是一个坏主意。

  1. xmlStrlen() 不会做你认为的那样。它计算字符串中 UTF-8 代码单元(也称为字节)的数量。它不计算字符数。这是documentation 中的所有内容。

  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. 1234563您可以将"WCHAR_T" 指定为iconv() 的编码之一。

记住:你可能不想要wchar_tstd::wstringwchar_t 的可移植性没有用,使其有用也不能移植。生活吧。

【讨论】:

    【解决方案3】:

    添加

    #include

    1. 将 xmlChar* 转换为字符串

    std::string strGbk((char*)node);

    1. 将字符串转换为 wstring

    std::string strGbk = "中国永远强大";

    std::wstring wstr = boost::locale::conv::to_utf<wchar_t>(strGbk, "gbk");
    std::cout << strGbk << std::endl;
    std::wcout << wstr. << std::endl;
    
    1. 它对我有用,祝你好运。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-09-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-02
      相关资源
      最近更新 更多