2

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;

libxml2C 库一样,没有提供std::wstringxmlChar *. 我想知道在 C++11 中转换为 a的谨慎方法是否是通过类似这样的方法使用mbstowcs C 函数(正在进行中):xmlChar *std::wstring

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用于存储字符串的数量;我知道这不是字符数,而是unsigned char. 如果我给它命名,它会不会那么混乱byteLength,但我认为它会更清楚,因为我同时拥有charLengthwcharLength。至于代码的正确性,wideBuffer 总是会大于或等于保存缓冲区所需的大小(我相信)。作为需要更多空间而不是wide_t被截断的字符(我认为)。

4

3 回答 3

6

xmlStrlen()xmlChar*返回字符串中 UTF-8 编码的代码单元的数量。这与wchar_t转换数据时所需的编码代码单元数量不同,因此不要xmlStrlen()用于分配wchar_t字符串的大小。您需要调用std::mbtowc()一次以获得正确的长度,然后分配内存,并mbtowc()再次调用以填充内存。您还必须使用std::setlocale()to 告诉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 转换。

于 2013-01-01T02:13:02.293 回答
2

这段代码中存在一些问题,除了您正在使用wchar_t而且std::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 iconv和手册)。您可以将其指定为.man 3 iconv_openman 3 iconv_close"WCHAR_T"iconv()

记住:你可能不想要wchar_tstd::wstring。可移植的东西wchar_t没有用,使它有用也不能移植。这就是生活。

于 2013-01-01T02:04:24.067 回答
0

添加

#include <boost/locale.hpp>

  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. 它对我有用,祝你好运。
于 2021-09-07T12:57:10.500 回答