webentwicklung-frage-antwort-db.com.de

UTF8 zu / von Wide Char Konvertierung in AWL

Ist es möglich, einen UTF8-String in einem std :: string in einen std :: wstring und umgekehrt plattformunabhängig umzuwandeln? In einer Windows-Anwendung würde ich MultiByteToWideChar und WideCharToMultiByte verwenden. Der Code ist jedoch für mehrere Betriebssysteme kompiliert, und ich beschränke mich auf die Standard-C++ - Bibliothek.

68

Ich habe diese Frage vor 5 Jahren gestellt. Dieser Thread war damals für mich sehr hilfreich, ich kam zu einem Schluss, dann ging ich mit meinem Projekt weiter. Es ist lustig, dass ich in letzter Zeit etwas Ähnliches gebraucht habe, das in keiner Beziehung zu diesem Projekt aus der Vergangenheit stand. Als ich nach möglichen Lösungen suchte, 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, sind jetzt Teil des Standards. Wenn wir std :: wstring durch den neuen String-Typ std :: u16string ersetzen, sehen die Konvertierungen folgendermaßen 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 nehme ich keine akzeptierte Antwort.

43
25
Assaf Lavie

Sie können extrahieren utf8_codecvt_facet from 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.

23
Constantin

Die Problemdefinition gibt explizit an, dass die 8-Bit-Zeichencodierung UTF-8 ist. Das macht dies zu einem trivialen Problem. Alles, was es braucht, ist ein bisschen herumzudrehen, um von einer UTF-Spezifikation in eine andere zu konvertieren.

Schauen Sie sich die Kodierungen auf diesen Wikipedia-Seiten für TF-8 , TF-16 und TF-32 an.

Das Prinzip ist einfach: Gehen Sie die Eingabe durch und setzen Sie einen 32-Bit-Unicode-Codepunkt gemäß einer UTF-Spezifikation zusammen. Geben Sie dann den Codepunkt gemäß der anderen Spezifikation aus. Die einzelnen Codepunkte benötigen keine Übersetzung, wie dies bei jeder anderen Zeichencodierung erforderlich wäre. das ist es, was dies zu einem einfachen Problem macht.

Hier ist eine schnelle Implementierung von wchar_t Zu UTF-8-Konvertierung und umgekehrt. Es wird davon ausgegangen, dass die Eingabe bereits richtig codiert ist - hier gilt das alte Sprichwort "Garbage in, Garbage out". Ich glaube, dass die Überprüfung der Codierung am besten als separater Schritt erfolgt.

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, da die Bereiche d800 Bis dfff ungültige Codepunkte sind. Sie zeigen an, dass Sie UTF-16 dekodieren. Wenn Sie wissen, dass wchar_t 32 Bit ist, können Sie 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;
}

Wenn Sie wieder wissen, dass wchar_t 32 Bit ist, können Sie Code aus dieser Funktion entfernen, aber in diesem Fall sollte es keinen Unterschied machen. Der Ausdruck sizeof(wchar_t) > 2 ist zur Kompilierungszeit bekannt, sodass jeder anständige Compiler toten Code erkennt und entfernt.

16
Mark Ransom

Es gibt verschiedene Möglichkeiten, dies zu tun, aber die Ergebnisse hängen davon ab, wie die Zeichencodierungen in den Variablen string und wstring sind.

Wenn Sie wissen, dass string ASCII ist, können Sie einfach den Iteratorkonstruktor von wstring verwenden:

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

Wenn Ihr string jedoch eine andere Kodierung hat, erhalten Sie sehr schlechte Ergebnisse. Wenn die Codierung Unicode ist, können Sie sich das ICU-Projekt ansehen, das eine plattformübergreifende Sammlung von Bibliotheken bereitstellt, die in und von allen Arten von Unicode-Codierungen konvertiert werden.

Wenn Ihr string Zeichen in einer Codepage enthält, hat $ DEITY möglicherweise Mitleid mit Ihrer Seele.

13
Ben Straub

ConvertUTF.hConvertUTF.c

Dank an bames5 für die Bereitstellung aktualisierter Versionen

4
vharron

Sie können die Locale-Facette codecvt verwenden. Es ist eine bestimmte Spezialisierung definiert, codecvt<wchar_t, char, mbstate_t>, das für Sie von Nutzen sein kann, dessen Verhalten jedoch systemspezifisch ist und in keiner Weise die Konvertierung nach UTF-8 garantiert.

2

TFConverter - checke diese Bibliothek aus. Es führt eine solche Konvertierung durch, aber Sie benötigen auch die ConvertUTF-Klasse - ich habe sie gefunden hier

1
Trisch

Hat meine eigene Bibliothek für die Konvertierung von utf-8 nach utf-16/utf-32 erstellt - hat sich jedoch dafür entschieden, ein Fork des vorhandenen Projekts für diesen Zweck zu erstellen.

https://github.com/tapika/cutf

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

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

Funktionsprototypen sehen folgendermaßen aus: (Eine vollständige Liste finden Sie unter 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 holen Sie sich eine schwerere Bibliothek, die Ihnen gefällt.

0
TarmoPikaro