2017-02-25 4 views
1

Wie kann ich std::codecvt_utf8_utf16 verwenden, um von uft8 nach utf16 und zurück ohne mit einer beliebigen String-Klasse wie std::string oder std::wstring zu konvertieren, aber nur einfache Arrays und Literal-Strings? Wie kann ich die richtige Puffergröße ermitteln, um die Konvertierung zu speichern?Wie kann ich std :: codecvt_utf8_utf16 verwenden, um zu und von utf8 ohne String-Klasse zu konvertieren?

Zum Beispiel dieser Schnittstelle erfüllen:

std::unique_ptr<char16_t[]> ToUTF16(const char* utf8String); 
std::unique_ptr<char[]> ToUTF8(const char16_t* utf16String); 
+0

Die Größe Informationen verloren gehen mit Zeigern, so können Sie nicht. –

+2

Warum möchten Sie keine String-Klassen verwenden? Dies wird zu einem Kinderspiel, indem Sie 'std :: wstring_convert' verwenden, um die Konvertierung zu erleichtern, und Sie können Literale und Arrays als Eingabe übergeben. Die Verwendung von 'std :: string' und' std :: u16string' ist einfacher als die Verwendung von 'std :: unique_ptr'-Arrays. Zumindest, wenn Sie ein 'std :: unique_ptr'-Array zurückgeben müssen, können Sie' std :: wstring_convert' für die Konvertierung verwenden und dann die resultierenden Zeichenfolgen in ein Ausgabe-Array kopieren. Die Array-Größe entspricht der Zeichenfolgengröße. –

+0

@RemyLebeau Ich benutze benutzerdefinierte Container (Strings, Vektoren usw., die nicht STL sind) aufgrund der hohen Anforderungen an die Speicherverwaltung wie Stateful Allokatoren. –

Antwort

3

Sie können dies tun, indem Sie die codecvt_utf8_utf16 members directly verwenden. Der erste Schritt besteht darin, die Länge der Eingabe mit strlen zu finden (vorausgesetzt, es ist NUL beendet). codecvt Mitglieder arbeiten aus Bereichen, so müssen Sie wissen, wie groß Ihre Eingabe ist.

Es tritt jedoch ein Problem auf: die Länge des Ausgabepuffers. Während codecvt über ein length Mitglied verfügt, berechnet es nur die Länge für Konvertierungen, die in verwenden. Das heißt, Konvertierungen von UTF-8 zu UTF-16. Es gibt keine Längenmethode für die andere Konvertierung.

Daher ist die einzige Möglichkeit, dies zu handhaben, einige der Daten in einen Puffer bekannter Größe zu konvertieren. Wenn die Konvertierung nicht vollständig abgeschlossen ist, konvertieren Sie weitere Daten. Nach all dem, was du getan hast, lege alle Teile in einen Puffer, jetzt wo du weißt, wie viele Charaktere dort sein werden.

Während Ihre Frage sagt, dass Sie nicht wollen, Strings verwenden, werde ich vector<T> dafür verwenden, weil, wenn ich nicht, ich habe gerade vector Umschreiben sein würde. Und dafür gibt es keinen Grund.

std::unique_ptr<char16_t[]> ToUTF16(const char* utf8String) 
{ 
    auto end_ptr = utf8String + std::char_traits<char>::length(utf8String); 
    std::codecvt_utf8_utf16<char16_t> converter; 
    std::codecvt_utf8_utf16<char16_t>::state_type state; 

    std::array<char16_t, buffer_size> buffer; 
    std::vector<char16_t> storage; 

    auto curr_in_ptr = utf8String; 
    auto out_loc = buffer.begin(); 

    do 
    { 
     std::codecvt_base::result rslt = converter.in(state, 
      curr_in_ptr, end_ptr, curr_in_ptr, 
      buffer.begin(), buffer.end(), out_loc); 

     storage.insert(storage.end(), buffer.begin(), out_loc); 
    } 
    while(curr_in_ptr != end_ptr); 

    //+1 for NUL terminator. 
    std::unique_ptr<char16_t[]> ret(new char16_t[storage.size() + 1]); 
    std::copy(storage.begin(), storage.end(), ret.get()); 
    ret.get()[storage.size()] = char16_t(); 
    return ret; 
} 

Der andere Code funktioniert auf die gleiche Art und Weise, mit der Ausnahme, dass in wird out und die char16_t 's und char' s vertauscht.

+2

_wcstrlen funktioniert nicht, da char16_t nicht wchar_t_ ... verwendet 'std :: char_traits :: Länge (str)' – zett42

+0

@ zett42: Ausgezeichnet. Vielen Dank. –

+0

Ich schlage nur vor, eine Überprüfung für 'std :: codecvt_base :: result' im Falle von' error' in der while-Schleife hinzuzufügen und die unbenutzte 'alte' Variable zu entfernen :). –

Verwandte Themen