Ist es möglich, einen UTF8-String in einem std :: string in einen std :: wstring und umgekehrt plattformunabhängig zu konvertieren? In einer Windows-Anwendung würde ich MultiByteToWideChar und WideCharToMultiByte verwenden. Der Code ist jedoch für mehrere Betriebssysteme kompiliert und ich bin auf die Standard-C ++ - Bibliothek beschränkt.
75
std::wstring
iststd::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.Antworten:
Ich habe diese Frage vor 5 Jahren gestellt. Dieser Thread war damals sehr hilfreich für mich, ich kam zu einem Schluss, dann ging ich mit meinem Projekt weiter. Es ist lustig, dass ich in letzter Zeit etwas Ähnliches brauchte, völlig unabhängig von diesem Projekt aus der Vergangenheit. Als ich nach möglichen Lösungen suchte, stieß ich auf meine eigene Frage :)
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 verzichte ich darauf, eine akzeptierte Antwort zu wählen.
quelle
UTF8-CPP: UTF-8 mit C ++ auf tragbare Weise
quelle
Sie können
utf8_codecvt_facet
aus der Boost-Serialisierungsbibliothek extrahieren .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 Sie nach
utf8_codecvt_facet.hpp
undutf8_codecvt_facet.cpp
Dateien in Boost-Quellen.quelle
Die Problemdefinition besagt ausdrücklich, dass die 8-Bit-Zeichencodierung UTF-8 ist. Das macht dies zu einem trivialen Problem. Für die Konvertierung von einer UTF-Spezifikation in eine andere ist lediglich ein wenig Aufwand erforderlich.
Schauen Sie sich einfach die Codierungen auf diesen Wikipedia-Seiten für UTF-8 , UTF-16 und UTF-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 Zeichenkodierung erforderlich wäre. Das macht dies zu einem einfachen Problem.
Hier ist eine schnelle Implementierung der
wchar_t
UTF-8-Konvertierung und umgekehrt. Es wird davon ausgegangen, dass die Eingabe bereits ordnungsgemäß codiert ist - hier gilt das alte Sprichwort "Müll rein, Müll raus". 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, einfach weil der Bereich
d800
durchdfff
ungültige Codepunkte ist. Sie zeigen an, dass Sie UTF-16 dekodieren. Wenn Sie wissen, dass dieswchar_t
32 Bit sind, 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 wissen, dass dies
wchar_t
32 Bit sind, können Sie Code aus dieser Funktion entfernen, aber in diesem Fall sollte dies keinen Unterschied machen. Der Ausdrucksizeof(wchar_t) > 2
ist zur Kompilierungszeit bekannt, sodass jeder anständige Compiler toten Code erkennt und entfernt.quelle
Es gibt verschiedene Möglichkeiten, dies zu tun, aber die Ergebnisse hängen davon ab, welche Zeichencodierungen in den Variablen
string
und enthaltenwstring
sind.Wenn Sie wissen, dass
string
es sich um ASCII handelt, können Sie einfachwstring
den Iterator-Konstruktor verwenden:string s = "This is surely ASCII."; wstring w(s.begin(), s.end());
Wenn Sie
string
jedoch eine andere Codierung haben, erhalten Sie sehr schlechte Ergebnisse. Wenn die Codierung Unicode ist, können Sie sich das ICU-Projekt ansehen , das einen plattformübergreifenden Satz von Bibliotheken bereitstellt, die in und aus allen Arten von Unicode-Codierungen konvertieren.Wenn Ihr
string
Zeichen in einer Codepage enthält, kann $ DEITY Ihrer Seele gnädig sein.quelle
ConvertUTF.h ConvertUTF.c
Dank an bames53 für die Bereitstellung aktualisierter Versionen
quelle
Sie können die
codecvt
Gebietsschemafacette verwenden . Es ist eine spezielle Spezialisierung definiert,codecvt<wchar_t, char, mbstate_t>
die für Sie von Nutzen sein kann. Das Verhalten ist jedoch systemspezifisch und garantiert in keiner Weise die Konvertierung auf UTF-8.quelle
encoding
statt zu konfigurierenlocale
. Soweit ich das beurteilen kann, gibt es kein solches Gebietsschema, das jedes einzelne Unicode-Zeichen darstellen kann. Angenommen, ich möchte eine Zeichenfolge codieren, die alle Unicode-Zeichen enthält. Welches Gebietsschema schlagen Sie mir zur Konfiguration vor? Korrigiere mich, wenn ich falsch liege.UTFConverter - Überprüfen Sie diese Bibliothek. Es führt eine solche Konvertierung durch, aber Sie benötigen auch die ConvertUTF-Klasse - ich habe sie hier gefunden
quelle
Ich habe meine eigene Bibliothek für die Konvertierung von utf-8 in utf-16 / utf-32 erstellt - aber beschlossen, zu diesem Zweck einen Teil des vorhandenen Projekts zu erstellen.
https://github.com/tapika/cutf
(Entstanden von https://github.com/noct/cutf )
Die API funktioniert sowohl mit 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 Ihren Anforderungen nicht entspricht, können Sie den folgenden Link öffnen:
http://utf8everywhere.org/
Scrollen Sie am Ende der Seite nach unten und wählen Sie eine beliebige schwerere Bibliothek aus.
quelle
Ich glaube nicht, dass es einen tragbaren Weg gibt, dies zu tun. C ++ kennt die Codierung seiner Multibyte-Zeichen nicht.
Wie Chris vorgeschlagen hat, ist es am besten, mit Codecvt zu spielen.
quelle