UTF8 aan/uit breed char conversie in STL

Is het mogelijk om te zetten in UTF8-string in een std::string std::wstring en vice versa in een platform-onafhankelijke manier? In een Windows-toepassing die ik zou gebruiken MultiByteToWideChar en WideCharToMultiByte. Echter, de code is opgesteld voor meerdere Os ‘ en ik ben beperkt tot standard C++ – bibliotheek.

  • Overigens, de standaard C++ – bibliotheek is niet genoemd STL; de STL wordt slechts een klein onderafdeling van de standaard C++ – bibliotheek. In dit geval geloof ik dat je het vragen om functionaliteit in de standaard C++ – bibliotheek, en ik heb beantwoord.
  • Je hebt niet aangegeven met welke codering je wilt om te eindigen met. wstring niet opgeven van een bepaalde codering. Natuurlijk zou het natuurlijk om te zetten naar utf32 op platforms waar wchar_t maxlen is 4 bytes groot, en utf16 als wchar_t maxlen is 2 bytes. Is dat wat je wilt?



10 Replies
  1. 43

    Ik heb deze vraag gesteld op 5 jaar geleden. Deze draad was erg nuttig voor mij toen, kwam ik tot een conclusie, daarna verhuisde ik met mijn project. Het is grappig dat ik iets nodig had soortgelijke onlangs, geheel los van dat project uit het verleden. Als ik onderzoek deed naar mogelijke oplossingen, struikelde ik over mijn eigen vraag 🙂

    De oplossing die ik koos is nu gebaseerd op C++11. De boost libraries dat Constantin vermeldt in zijn antwoord zijn nu onderdeel van de standaard. Als wij in de plaats van std::wstring met de nieuwe string type std::u16string, dan is de conversie zal er als volgt uitzien:

    UTF-8 en UTF-16

    std::string source;
    ...
    std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> convert;
    std::u16string dest = convert.from_bytes(source);    

    UTF-16 en UTF-8

    std::u16string source;
    ...
    std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> convert;
    std::string dest = convert.to_bytes(source);    

    Gezien vanaf de andere antwoorden, er zijn meerdere benaderingen van het probleem. Dat is de reden waarom ik afzien van het kiezen van een geaccepteerd antwoord.

    • wstring betekent 2 of 4 bytes in plaats van enkel-byte-tekens. Waar is de vraag om te schakelen van de utf8-codering?
    • Ik heb een aantal vreemde slechte prestaties met codecvt, kijk hier voor details: stackoverflow.com/questions/26196686/…
    • Ik denk dat je moet accepteren dat dit antwoord. Zeker, er zijn meerdere manieren om dit op te lossen, maar dit is de enige draagbare oplossing die het niet nodig hebben van een bibliotheek.
    • Is dit UTF-16 met LE of WORDEN?
    • std::wstring_convert afgeschaft in C++17
  2. 23

    U kunt het uittreksel utf8_codecvt_facet van Boost-serialization-bibliotheek.

    Hun voorbeeld van gebruik:

      typedef wchar_t ucs4_t;
    
      std::locale old_locale;
      std::locale utf8_locale(old_locale,new utf8_codecvt_facet<ucs4_t>);
    
      //Set a New global locale
      std::locale::global(utf8_locale);
    
      //Send the UCS-4 data out, converting to UTF-8
      {
        std::wofstream ofs("data.ucd");
        ofs.imbue(utf8_locale);
        std::copy(ucs4_data.begin(),ucs4_data.end(),
              std::ostream_iterator<ucs4_t,ucs4_t>(ofs));
      }
    
      //Read the UTF-8 data back in, converting to UCS-4 on the way in
      std::vector<ucs4_t> from_file;
      {
        std::wifstream ifs("data.ucd");
        ifs.imbue(utf8_locale);
        ucs4_t item = 0;
        while (ifs >> item) from_file.push_back(item);
      }

    Kijk voor utf8_codecvt_facet.hpp en utf8_codecvt_facet.cpp bestanden in de boost-bronnen.

    • Ik dacht dat je moest bijbrengen van de stroom, voordat het wordt geopend, anders is het bijbrengen van wordt genegeerd!
    • Martin, het lijkt te werken met Visual Studio 2005: 0x41a is geconverteerd naar {0xd0, 0x9a} UTF-8 reeks.
  3. 17

    De probleem definitie is uitdrukkelijk bepaald dat de 8-bit karakter codering is UTF-8. Dat maakt dit een triviaal probleem; het enige dat nodig is een beetje-twiddling te zetten van de ene UTF de bonnefooi naar de andere.

    Kijk maar naar de coderingen op deze Wikipedia pagina ‘ s zijn voor UTF-8, UTF-16, en UTF-32.

    Het principe is eenvoudig – ga door de ingang en het opstellen van een 32-bits Unicode-code-point volgens een UTF spec, dan stralen de code volgens de andere spec. De individuele code punten geen vertaling nodig, wat nodig zou zijn met een ander karakter codering; dat is wat maakt dit een simpel probleem.

    Hier is een snelle uitvoering van wchar_t naar UTF-8 conversie en vice versa. Het gaat ervan uit dat de input is al goed gecodeerd – het oude gezegde: “Garbage in, Garbage out’ is hier van toepassing. Ik geloof dat het verifiëren van de codering gebeurt best in een aparte stap maken.

    std::string wchar_to_UTF8(const wchar_t * in)
    {
        std::string out;
        unsigned int codepoint = 0;
        for (in;  *in != 0;  ++in)
        {
            if (*in >= 0xd800 && *in <= 0xdbff)
                codepoint = ((*in - 0xd800) << 10) + 0x10000;
            else
            {
                if (*in >= 0xdc00 && *in <= 0xdfff)
                    codepoint |= *in - 0xdc00;
                else
                    codepoint = *in;
    
                if (codepoint <= 0x7f)
                    out.append(1, static_cast<char>(codepoint));
                else if (codepoint <= 0x7ff)
                {
                    out.append(1, static_cast<char>(0xc0 | ((codepoint >> 6) & 0x1f)));
                    out.append(1, static_cast<char>(0x80 | (codepoint & 0x3f)));
                }
                else if (codepoint <= 0xffff)
                {
                    out.append(1, static_cast<char>(0xe0 | ((codepoint >> 12) & 0x0f)));
                    out.append(1, static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f)));
                    out.append(1, static_cast<char>(0x80 | (codepoint & 0x3f)));
                }
                else
                {
                    out.append(1, static_cast<char>(0xf0 | ((codepoint >> 18) & 0x07)));
                    out.append(1, static_cast<char>(0x80 | ((codepoint >> 12) & 0x3f)));
                    out.append(1, static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f)));
                    out.append(1, static_cast<char>(0x80 | (codepoint & 0x3f)));
                }
                codepoint = 0;
            }
        }
        return out;
    }

    De bovenstaande code werkt voor zowel UTF-16 en UTF-32-ingang, gewoon omdat het bereik d800 door dfff zijn ongeldige code punten; ze geven aan dat je het decoderen van UTF-16. Als u weet dat wchar_t is 32 bits dan kon je er een paar verwijderen code voor het optimaliseren van de functie.

    std::wstring UTF8_to_wchar(const char * in)
    {
        std::wstring out;
        unsigned int codepoint;
        while (*in != 0)
        {
            unsigned char ch = static_cast<unsigned char>(*in);
            if (ch <= 0x7f)
                codepoint = ch;
            else if (ch <= 0xbf)
                codepoint = (codepoint << 6) | (ch & 0x3f);
            else if (ch <= 0xdf)
                codepoint = ch & 0x1f;
            else if (ch <= 0xef)
                codepoint = ch & 0x0f;
            else
                codepoint = ch & 0x07;
            ++in;
            if (((*in & 0xc0) != 0x80) && (codepoint <= 0x10ffff))
            {
                if (sizeof(wchar_t) > 2)
                    out.append(1, static_cast<wchar_t>(codepoint));
                else if (codepoint > 0xffff)
                {
                    out.append(1, static_cast<wchar_t>(0xd800 + (codepoint >> 10)));
                    out.append(1, static_cast<wchar_t>(0xdc00 + (codepoint & 0x03ff)));
                }
                else if (codepoint < 0xd800 || codepoint >= 0xe000)
                    out.append(1, static_cast<wchar_t>(codepoint));
            }
        }
        return out;
    }

    Weer als je weet dat wchar_t is 32 bits je kon verwijder enkele code van deze functie, maar in dit geval zou het geen verschil maken. De uitdrukking sizeof(wchar_t) > 2 is bekend tijdens het compileren, zodat een fatsoenlijke compiler herkent de dode code en verwijder deze.

    • Ik zie niet dat hij seaid iets over std::string met de UTF-8 gecodeerde strings in de oorspronkelijke vraag: “Is het mogelijk om te zetten std::string std::wstring en vice versa in een platform-onafhankelijke manier?”
    • UTF-8 is aangegeven in de titel van de post. Je hebt gelijk dat het ontbreken van de body van de tekst.
    • Doh, je hebt gelijk. UTF8-CPP dan 🙂
    • Dank je voor de correctie, ik heb voornemens om gebruik te maken UTF8. Ik bewerkte de vraag naar meer duidelijk.
    • Maar “widechar” betekent niet noodzakelijk UTF16
    • Wat heb je misschien een goed “proof of concept”. Het is één ding om te zetten geldig coderingen met succes. Het is een ander niveau van inspanning te hanteren conversie van nietige of ongeldige het coderen van gegevens (bijv. ongepaarde surrogaten in UTF-16) correct en volgens de specificaties. Voor dat je moet echt wat meer grondig ontworpen en geteste code.
    • McQueen, je hebt helemaal gelijk. Ik maakte de veronderstelling dat de codering was al goed, en het was slechts een mechanische omzetting. Ik weet zeker dat er zijn situaties waarin dat het geval is, en deze code op adequate wijze zou worden – maar de beperkingen dienen expliciet te worden vermeld. Het is niet duidelijk uit de oorspronkelijke vraag als dit een probleem is of niet.
    • Ik heb hetzelfde gevoel als jij. De vragen reeds staten “UTF8”, dus het is een codering/decodering probleem. Het heeft niets te maken met de landinstelling. Waarvan het antwoord vermeld landinstelling niet de zin om.
    • na al die jaren dat ik besefte hoe dicht dit was werkzaam voor zowel de UTF-16 en UTF-32 wchar_t. Ik heb bijgewerkt het antwoord.

  4. 13

    Er zijn verschillende manieren om dit te doen, maar de resultaten zijn afhankelijk van wat de karakters worden in de string en wstring variabelen.

    Als u weet dat de string ASCII is, je kan gewoon gebruik maken van wstring’s iterator constructor:

    string s = "This is surely ASCII.";
    wstring w(s.begin(), s.end());

    Als uw string heeft een aantal andere codering, maar krijg je erg slechte resultaten. Indien de codering op Unicode, je kon een kijkje nemen op de ICU project, die voorziet in een cross-platform verzameling bibliotheken voor het omzetten naar en van allerlei Unicode-coderingen.

    Als uw string bevat tekens in een code pagina, dan kan $GODHEID genadig zijn op uw ziel.

    • ICU converteert te/van elk karakter codering die ik ooit heb tegengekomen. Zijn enorm.
  5. 2

    Kunt u gebruik maken van de codecvt landinstelling facet. Er is een specifieke specialisatie gedefinieerd, codecvt<wchar_t, char, mbstate_t> die van nut kan zijn voor u, hoewel, het gedrag van dat systeem-specifieke, en garandeert niet dat de conversie naar UTF-8 op geen enkele manier.

    • Het doen van coderen/decoderen volgens de landinstelling is een slecht idee. Net zoals u zei: “geen garantie”.
    • uiteraard moet configureren std::landinstelling instantie speciaal voor de gewenste conversie.
    • Ik denk nog steeds dat het gebruik van de landinstelling voor het coderen/decoderen is verkeerd. De juiste manier is om het configureren van encoding in plaats van locale. Voor zover ik kan vertellen, er is geen een landinstelling die kan vertegenwoordigen elke enkel unicode-teken. Laten we zeggen dat ik wil coderen een string met alle unicode-tekens, die landinstelling u sugguest mij te configureren? Corret me als ik verkeerd ben.
    • Landinstelling in C++ is een zeer abstract begrip dat betrekking heeft op veel meer dingen dan alleen regionale instellingen en coderingen. In principe kan men.doe alles met het. Terwijl codecvt_facet inderdaad behandelt meer dan alleen maar een simpele hercoderen, absoluut niets verhindert dat het maken van eenvoudige unicode-transformaties.
  6. 0

    Gemaakt van mijn eigen bibliotheek voor utf-8 en utf-16/utf-32 conversie – maar besloten om een afsplitsing van een bestaand project voor dat doel.

    https://github.com/tapika/cutf

    (Afkomstig uit https://github.com/noct/cutf )

    API werkt met gewone C als C++.

    Functie prototypes ziet er als volgt uit: (Voor de volledige lijst zie https://github.com/tapika/cutf/blob/master/cutf.h )

    //
    // Converts utf-8 string to wide version.
    //
    // returns target string length.
    //
    size_t utf8towchar(const char* s, size_t inSize, wchar_t* out, size_t bufSize);
    
    //
    // Converts wide string to utf-8 string.
    //
    // returns filled buffer length (not string length)
    //
    size_t wchartoutf8(const wchar_t* s, size_t inSize, char* out, size_t outsize);
    
    #ifdef __cplusplus
    
    std::wstring utf8towide(const char* s);
    std::wstring utf8towide(const std::string& s);
    std::string  widetoutf8(const wchar_t* ws);
    std::string  widetoutf8(const std::wstring& ws);
    
    #endif

    Voorbeeld van gebruik en /of eenvoudige test applicatie voor utf-conversie testen:

    #include "cutf.h"
    
    #define ok(statement)                                       \
        if( !(statement) )                                      \
        {                                                       \
            printf("Failed statement: %s\n", #statement);       \
            r = 1;                                              \
        }
    
    int simpleStringTest()
    {
        const wchar_t* chineseText = L"主体";
        auto s = widetoutf8(chineseText);
        size_t r = 0;
    
        printf("simple string test:  ");
    
        ok( s.length() == 6 );
        uint8_t utf8_array[] = { 0xE4, 0xB8, 0xBB, 0xE4, 0xBD, 0x93 };
    
        for(int i = 0; i < 6; i++)
            ok(((uint8_t)s[i]) == utf8_array[i]);
    
        auto ws = utf8towide(s);
        ok(ws.length() == 2);
        ok(ws == chineseText);
    
        if( r == 0 )
            printf("ok.\n");
    
        return (int)r;
    }

    En als deze bibliotheek niet voldoen aan uw wensen – voel je vrij om te openen de volgende link:

    http://utf8everywhere.org/

    en scroll naar beneden op het einde van een pagina en pick-up een zwaardere bibliotheek die u wilt.

  7. -1

    Ik denk niet dat er een draagbare manier om dit te doen. C++ kent niet de codering van de multibyte-tekens.

    Als Chris voorgesteld, uw beste inzet is om te spelen met codecvt.

    • De vraag zegt “UTF8”, dus “de codering van de multibyte-tekens” is bekend.

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *