2 votes

Libxml2 xmlChar * en std::wstring

libxml2 semble stocker toutes ses chaînes en UTF-8, en tant que xmlChar *.

/**
 * xmlChar:
 *
 * Il s'agit d'un octet de base dans une chaîne encodée en UTF-8.
 * Il est non signé, ce qui permet de repérer les cas où char * est assigné
 * à xmlChar * (rendant éventuellement impossible une sérialisation inverse).
 */
typedef unsigned char xmlChar;

Comme libxml2 est une bibliothèque C, il n'y a pas de routines fournies pour obtenir un std::wstring à partir d'un xmlChar *. Je me demande si le moyen prudent de convertir un xmlChar * en un std::wstring en C++11 est d'utiliser la fonction C mbstowcs, via quelque chose comme ceci (travail en cours) :

std::wstring xmlCharToWideString(const xmlChar *xmlString) {
    if(!xmlString){abort();} //la chaîne fournie était nulle
    int charLength = xmlStrlen(xmlString); //exclut le terminateur null
    wchar_t *wideBuffer = new wchar_t[charLength];
    size_t wcharLength = mbstowcs(wideBuffer, (const char *)xmlString, charLength);
    if(wcharLength == (size_t)(-1)) {abort();} //mbstowcs a échoué
    std::wstring wideString(wideBuffer, wcharLength);
    delete[] wideBuffer;
    return wideString;
}

Éditer : Juste pour information, je suis très conscient de ce que retourne xmlStrlen; c'est le nombre d' xmlChar utilisés pour stocker la chaîne; je sais que ce n'est pas le nombre de caractères mais plutôt le nombre d' unsigned char. Cela aurait été moins confus si je l'avais nommé byteLength, mais je pensais que ce serait plus clair car j'ai à la fois charLength et wcharLength. En ce qui concerne la correction du code, le wideBuffer sera plus grand ou égal à la taille requise pour contenir le tampon, toujours (je crois). Les caractères nécessitant plus d'espace que wide_t seront tronqués (je pense).

6voto

Remy Lebeau Points 130112

xmlStrlen() renvoie le nombre d'unités de code encodées en UTF-8 dans la chaîne xmlChar*. Ce nombre ne sera pas le même que le nombre d'unités de code encodées en wchar_t nécessaires lorsque les données sont converties, donc ne pas utiliser xmlStrlen() pour allouer la taille de votre chaîne wchar_t. Vous devez appeler std::mbtowc() une fois pour obtenir la longueur correcte, puis allouer la mémoire, et appeler mbtowc() de nouveau pour remplir la mémoire. Vous devrez également utiliser std::setlocale() pour indiquer à mbtowc() d'utiliser UTF-8 (manipuler la locale peut ne pas être une bonne idée, surtout si plusieurs threads sont impliqués). Par exemple:

std::wstring xmlCharToWideString(const xmlChar *xmlString)
{    
    if (!xmlString) { abort(); } //la chaîne fournie était nulle

    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); //exclut le caractère de terminaison nul
        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 a échoué
    }

    return wideString;
}

Une meilleure option, vu que vous mentionnez C++11, est d'utiliser std::codecvt_utf8 avec std::wstring_convert afin de ne pas avoir à gérer les locales:

std::wstring xmlCharToWideString(const xmlChar *xmlString)
{    
    if (!xmlString) { abort(); } //la chaîne fournie était nulle
    try
    {
        std::wstring_convert, wchar_t> conv;
        return conv.from_bytes((const char*)xmlString);
    }
    catch(const std::range_error& e)
    {
        abort(); //wstring_convert a échoué
    }
}

Une autre option est d'utiliser une bibliothèque Unicode réelle, comme ICU ou ICONV, pour gérer les conversions Unicode.

2voto

Dietrich Epp Points 72865

Il y a quelques problèmes dans ce code, en plus du fait que vous utilisez wchar_t et std::wstring ce qui est une mauvaise idée à moins que vous ne fassiez des appels à l'API Windows.

  1. xmlStrlen() ne fait pas ce que vous pensez qu'il fait. Il compte le nombre d'unités de code UTF-8 (aussi appelés octets) dans une chaîne. Il ne compte pas le nombre de caractères. Tout cela est expliqué dans la documentation.

  2. Compter les caractères ne vous donnera pas de manière portable la taille correcte d'un tableau wchar_t. Donc non seulement `xmlStrlen()` ne fait pas ce que vous pensez qu'il fait, ce que vous vouliez n'est pas non plus la bonne chose. Le problème est que l'encodage de `wchar_t` varie d'une plateforme à l'autre, le rendant totalement inutile pour du code portable.

`* La fonctionmbtowcs()` dépend de la locale. Elle ne convertit que depuis l'UTF-8 si la locale est une locale UTF-8 !

  • Ce code va provoquer une fuite de mémoire si le constructeur de std::wstring lance une exception. ``

``

Mes recommandations :

  1. Utilisez l'UTF-8 si possible. Le terrier du lapin wchar_t est beaucoup de travail supplémentaire pour aucun bénéfice (sauf la possibilité de faire des appels à l'API Windows).

  2. Si vous avez besoin d'UTF-32, utilisez std::u32string. Rappelez-vous que wstring a un encodage dépendant de la plateforme : il pourrait être un encodage à longueur variable (Windows) ou à longueur fixe (Linux, OS X).

  3. Si vous avez absolument besoin de wchar_t, alors il y a de fortes chances que vous soyez sur Windows. Voici comment le faire sur 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. Si vous avez absolument besoin de wchar_t et que vous n'êtes pas sur Windows, utilisez iconv() (voir man 3 iconv, man 3 iconv_open and man 3 iconv_close pour le manuel). Vous pouvez spécifier "WCHAR_T" comme un des encodages pour iconv().

Rappelez-vous : Vous ne voulez probablement pas de wchar_t ou de std::wstring. Ce que wchar_t fait de manière portable n'est pas utile, et le rendre utile n'est pas portable. C'est la vie.

``

0voto

herongwei Points 1

Ajouter

include

  1. convertir xmlChar* en string

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

  1. convertir string en wstring

std::string strGbk = "chine puissante pour toujours";

std::wstring wstr = boost::locale::conv::to_utf(strGbk, "gbk");
std::cout << strGbk << std::endl;
std::wcout << wstr. << std::endl;
  1. ça fonctionne pour moi, bonne chance.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X