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::wstring抜け出すためのルーチンは提供されていませんxmlChar *。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の両方があるのでもっとわかりやすいと思いました。コードの正確さに関しては、wideBufferは常にバッファーを保持するために必要なサイズ以上になります(私は信じています)。切り捨てられるよりも多くのスペースを必要とする文字として(私は思います)。charLengthwcharLengthwide_t

4

3 に答える 3

6

xmlStrlen()文字列内の UTF-8 でエンコードされたコードユニットの数を返しxmlChar*ます。wchar_tこれは、データが変換されるときに必要なエンコードされたコードユニットの数と同じにはならないため、文字列xmlStrlen()のサイズを割り当てるために使用しないでくださいwchar_t。一度呼び出しstd::mbtowc()て正しい長さを取得してからメモリを割り当て、mbtowc()もう一度呼び出してメモリを埋める必要があります。また、UTF-8 を使用std::setlocale()するように指示するために使用mbtowc()する必要があります (特に複数のスレッドが関係している場合は、ロケールをいじるのはお勧めできません)。例えば:

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_utf8withを使用する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
    }
}

別のオプションは、ICU や ICONV などの実際の Unicode ライブラリを使用して、Unicode 変換を処理することです。

于 2013-01-01T02:13:02.293 に答える
2

このコードにはいくつかの問題がありますが、Windows API を呼び出していない限りwchar_tstd::wstringこれは悪い考えです。

  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()参照してください)。のエンコーディングの 1 つとして指定できます。man 3 iconvman 3 iconv_openman 3 iconv_close"WCHAR_T"iconv()

wchar_t覚えておいてください: おそらくorは必要ありませんstd::wstring。移植可能であるものwchar_tは有用ではなく、有用にすることは移植可能ではありません。C'est la vie.

于 2013-01-01T02:04:24.067 に答える