UTF8 zu/von Breitzeichenkonvertierung in STL

Lesezeit: 10 Minuten

UTF8 zuvon Breitzeichenkonvertierung in STL
Wladimir Grigorow

Ist es möglich, UTF8-Strings in einem std::string plattformunabhängig in std::wstring und umgekehrt zu konvertieren? In einer Windows-Anwendung würde ich MultiByteToWideChar und WideCharToMultiByte verwenden. Der Code wird jedoch für mehrere Betriebssysteme kompiliert und ich bin auf die Standard-C++-Bibliothek beschränkt.

  • Die Standard-C++-Bibliothek heißt übrigens nicht STL; die STL ist nur ein kleiner Unterabschnitt der Standard-C++-Bibliothek. In diesem Fall fragen Sie meiner Meinung nach nach Funktionalität in der Standard-C++-Bibliothek, und ich habe entsprechend geantwortet.

    – Chris Jester-Young

    29. September 2008 um 12:09 Uhr

  • Sie haben nicht angegeben, mit welcher Codierung Sie enden möchten. wstring gibt keine bestimmte Codierung an. Natürlich wäre es natürlich, auf Plattformen, auf denen wchar_t 4 Byte breit ist, und utf16, wenn wchar_t 2 Byte groß ist, in utf32 zu konvertieren. Ist es das was du willst?

    – jalf

    11. November 2008 um 15:31 Uhr

  • @jalf Dein Kommentar ist irreführend. std::wstring ist std::basic_string<wchar_t>. wchar_t ist ein undurchsichtiger Datentyp, der ein Unicode-Zeichen darstellt (die Tatsache, dass es unter Windows 16 Bit lang ist, bedeutet nur, dass Windows nicht dem Standard folgt). Es gibt keine „Codierung“ für abstrakte Unicode-Zeichen, sie sind nur Zeichen.

    – kirelagin

    12. März 2020 um 20:35 Uhr

UTF8 zuvon Breitzeichenkonvertierung in STL
Wladimir Grigorow

Ich habe diese Frage vor 5 Jahren gestellt. Dieser Thread hat mir damals sehr geholfen, ich bin zu einem Ergebnis gekommen, dann habe ich mein Projekt weitergeführt. Es ist lustig, dass ich kürzlich etwas Ähnliches brauchte, völlig unabhängig von diesem Projekt aus der Vergangenheit. Als ich nach möglichen Lösungen recherchierte, bin ich auf meine eigene Frage gestoßen 🙂

Die Lösung, die ich jetzt gewählt habe, basiert auf C++11. Die Boost-Bibliotheken, die Constantin in seiner Antwort erwähnt, gehören mittlerweile zum Standard. Ersetzen wir std::wstring durch den neuen String-Typ std::u16string, dann sehen die Konvertierungen so aus:

UTF-8 bis 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 bis UTF-8

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

Wie aus den anderen Antworten hervorgeht, gibt es mehrere Ansätze für das Problem. Deshalb verzichte ich darauf, eine akzeptierte Antwort auszuwählen.

  • wstring impliziert 2 oder 4 Bytes anstelle von Einzelbytezeichen. Wo ist die Frage zum Wechseln von der utf8-Codierung?

    – Chawathe Vipul S

    25. April 2013 um 9:14 Uhr

  • Ich habe eine seltsam schlechte Leistung mit codecvt, siehe hier für Details: stackoverflow.com/questions/26196686/…

    – Xtra-Coder

    4. Oktober 2014 um 20:06 Uhr

  • Ist das UTF-16 mit LE oder BE?

    – Thomm

    14. Dezember 2015 um 14:46 Uhr

  • std::wstring_convert in C++17 veraltet

    – Hojjat Jafary

    19. Juni 2017 um 10:35 Uhr

  • @HojjatJafary, was ist der Ersatz?

    – jakar

    5. Februar 2020 um 21:40 Uhr

UTF8 zuvon Breitzeichenkonvertierung in STL
Mark Lösegeld

Die Problemdefinition besagt ausdrücklich, dass die 8-Bit-Zeichencodierung UTF-8 ist. Das macht dies zu einem trivialen Problem; Alles, was es erfordert, ist ein wenig Fummelei, um von einer UTF-Spezifikation in eine andere zu konvertieren.

Schauen Sie sich einfach die Kodierungen auf diesen Wikipedia-Seiten an UTF-8, UTF-16und UTF-32.

Das Prinzip ist einfach: Gehen Sie die Eingabe durch und stellen Sie einen 32-Bit-Unicode-Codepunkt gemäß einer UTF-Spezifikation zusammen und geben Sie dann den Codepunkt gemäß der anderen Spezifikation aus. Die einzelnen Codepunkte müssen nicht übersetzt werden, wie dies bei jeder anderen Zeichencodierung erforderlich wäre; das macht dies zu einem einfachen Problem.

Hier ist eine schnelle Implementierung von wchar_t in UTF-8-Konvertierung und umgekehrt. Es setzt voraus, dass die Eingabe bereits richtig kodiert ist – hier gilt das alte Sprichwort „Garbage in, Garbage out“. Ich glaube, dass die Überprüfung der Codierung am besten in einem separaten Schritt durchgeführt wird.

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;
}

Der obige Code funktioniert sowohl für UTF-16- als auch für UTF-32-Eingaben, einfach wegen des Bereichs d800 durch dfff sind ungültige Codepunkte; Sie zeigen an, dass Sie UTF-16 decodieren. Wenn Sie das wissen wchar_t 32 Bit ist, dann könnten Sie etwas Code entfernen, um die Funktion zu optimieren.

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;
}

Nochmals, wenn Sie das wissen wchar_t 32 Bit ist, könnten Sie etwas Code aus dieser Funktion entfernen, aber in diesem Fall sollte es keinen Unterschied machen. Der Ausdruck sizeof(wchar_t) > 2 ist zur Kompilierzeit bekannt, daher erkennt jeder anständige Compiler toten Code und entfernt ihn.

  • Ich sehe nicht, dass er in der ursprünglichen Frage etwas über std::string mit UTF-8-codierten Zeichenfolgen gesagt hat: “Ist es möglich, std::string plattformunabhängig in std::wstring und umgekehrt zu konvertieren?”

    – Nemanja Trifunovic

    29. September 2008 um 16:59 Uhr

  • UTF-8 ist im Titel des Beitrags angegeben. Sie haben Recht, dass es im Hauptteil des Textes fehlt.

    – Markieren Sie Lösegeld

    29. September 2008 um 18:07 Uhr

  • Aber ”widechar” bedeutet nicht unbedingt UTF16

    – Moogs

    16. Oktober 2008 um 10:23 Uhr

  • Was Sie haben, kann ein guter “Proof of Concept” sein. Es ist eine Sache, gültige Codierungen erfolgreich zu konvertieren. Es ist ein weiterer Aufwand, die Konvertierung ungültiger Codierungsdaten (z. B. ungepaarte Surrogate in UTF-16) gemäß den Spezifikationen korrekt zu handhaben. Dafür brauchen Sie wirklich gründlicher entworfenen und getesteten Code.

    – Craig McQueen

    23. Juli 2011 um 23:56 Uhr


  • @Craig McQueen, du hast vollkommen recht. Ich bin davon ausgegangen, dass die Codierung bereits korrekt war und es sich nur um eine mechanische Konvertierung handelte. Ich bin mir sicher, dass es Situationen gibt, in denen dies der Fall ist, und dieser Code wäre angemessen – aber die Einschränkungen sollten explizit angegeben werden. Aus der ursprünglichen Frage geht nicht hervor, ob dies ein Problem sein sollte oder nicht.

    – Markieren Sie Lösegeld

    24. Juli 2011 um 1:00 Uhr


UTF8 zuvon Breitzeichenkonvertierung in STL
Assaf Lavie

UTF8-CPP: UTF-8 mit C++ auf portable Weise

1647117614 404 UTF8 zuvon Breitzeichenkonvertierung in STL
Konstantin

Sie können extrahieren utf8_codecvt_facet von Boost-Serialisierungsbibliothek.

Ihr Anwendungsbeispiel:

  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);
  }

Suchen utf8_codecvt_facet.hpp und utf8_codecvt_facet.cpp Dateien in Boost-Quellen.

Es gibt mehrere Möglichkeiten, dies zu tun, aber die Ergebnisse hängen davon ab, welche Zeichencodierungen in der enthalten sind string und wstring Variablen.

Wenn Sie die kennen string ASCII ist, können Sie einfach verwenden wstringIterator-Konstruktor von :

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

Wenn dein string eine andere Codierung hat, erhalten Sie jedoch sehr schlechte Ergebnisse. Wenn die Codierung Unicode ist, können Sie einen Blick auf die werfen Projekt Intensivstationdas einen plattformübergreifenden Satz von Bibliotheken bereitstellt, die in und aus allen Arten von Unicode-Codierungen konvertieren.

Wenn dein string Zeichen in einer Codepage enthält, dann möge $DEITY deiner Seele gnädig sein.

  • ICU konvertiert auch / von jeder Zeichencodierung, die mir je begegnet ist. Es ist riesig.

    – Martin York

    29. September 2008 um 16:12 Uhr

Du kannst den … benutzen codecvt Gebietsschema-Facette. Es ist eine bestimmte Spezialisierung definiert, codecvt<wchar_t, char, mbstate_t> Das kann für Sie von Nutzen sein, obwohl das Verhalten systemspezifisch ist und in keiner Weise die Konvertierung in UTF-8 garantiert.

  • ICU konvertiert auch / von jeder Zeichencodierung, die mir je begegnet ist. Es ist riesig.

    – Martin York

    29. September 2008 um 16:12 Uhr

Erstellte meine eigene Bibliothek für die Konvertierung von utf-8 zu utf-16/utf-32 – entschied mich jedoch, zu diesem Zweck eine Verzweigung des bestehenden Projekts zu erstellen.

https://github.com/tapika/cutf

(Entstanden aus https://github.com/noct/cutf )

API funktioniert sowohl mit einfachem C als auch mit C++.

Funktionsprototypen sehen so aus: (Für eine vollständige Liste siehe 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

Beispielnutzung / einfache Testanwendung für UTF-Konvertierungstests:

#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;
}

Und wenn diese Bibliothek Ihre Anforderungen nicht erfüllt, können Sie den folgenden Link öffnen:

http://utf8everywhere.org/

und scrollen Sie am Ende der Seite nach unten und nehmen Sie eine schwerere Bibliothek, die Sie mögen.

995120cookie-checkUTF8 zu/von Breitzeichenkonvertierung in STL

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy