Domanda

Considera il seguente codice:

#include <string>
#include <fstream>
#include <iomanip>

int main() {
    std::string s = "\xe2\x82\xac\u20ac";
    std::ofstream out("test.txt");
    out << s.length() << ":" << s << std::endl;
    out << std::endl;
    out.close();
}
.

Sotto GCC 4.8 su Linux (Ubuntu 14.04), il file test.txt contiene questo:

6:€€
.

In Visual C ++ 2013 su Windows, contiene questo:

4:€\x80
.

(di '\ x80' intendo il singolo carattere a 8 bit 0x80).

Sono stato completamente in grado di ottenere entrambi i compilatore per emettere un carattere usando std::wstring.

Due domande:

    .
  • Cosa esattamente il compilatore Microsoft pensa che stia facendo con il char* letterale?Ovviamente sta facendo qualcosa per la codifica, ma ciò che non è chiaro.
  • Qual è il modo giusto per riscrivere il codice sopra utilizzando std::wstring e std::wofstream in modo che tu possa emetterà due caratteri ?
È stato utile?

Soluzione

Questo perché si utilizza \u20ac che è un carattere unicode Letterale in una stringa ASCII.

MSVC codifica "\xe2\x82\xac\u20ac" come 0xe2, 0x82, 0xac, 0x80, che è 4 caratteri stretti. Essenzialmente codifica \u20ac come 0x80 perché ha mappato il carattere dell'euro allo standard 1252 codepage .

GCC sta convertendo il /u20ac letterale Unicode in sequenza 3-byte UTF-8 0xe2, 0x82, 0xac in modo che la stringa risultante finisca come 0xe2, 0x82, 0xac, 0xe2, 0x82, 0xac.

Se si utilizza std::wstring = L"\xe2\x82\xac\u20ac" viene codificato da MSVC come 0xe2, 0x00, 0x82, 0x00, 0xac, 0x00, 0xac, 0x20 che è 4 caratteri larghi, ma dal momento che si sta mescolando un UTF-8 creato a mano con un UTF-16, la stringa risultante non ha molto senso. Se si utilizza un std::wstring = L"\u20ac\u20ac" ottieni 2 caratteri Unicode in un'ampia stringa come ti aspetteresti.

Il prossimo problema è che MSVC di OFSM e WoFstream scrivono sempre in ANSI / ASCII. Per arrivare a scrivere in UTF-8 dovresti usare <codecvt> (VS 2010 o successivo):

#include <string>
#include <fstream>
#include <iomanip>
#include <codecvt>

int main()
{
    std::wstring s = L"\u20ac\u20ac";

    std::wofstream out("test.txt");
    std::locale loc(std::locale::classic(), new std::codecvt_utf8<wchar_t>);
    out.imbue(loc);

    out << s.length() << L":" << s << std::endl;
    out << std::endl;
    out.close();
}
.

e scrivere UTF-16 (o più specificamente UTF-16LE):

#include <string>
#include <fstream>
#include <iomanip>
#include <codecvt>

int main()
{
    std::wstring s = L"\u20ac\u20ac";

    std::wofstream out("test.txt", std::ios::binary );
    std::locale loc(std::locale::classic(), new std::codecvt_utf16<wchar_t, 0x10ffff, std::little_endian>);
    out.imbue(loc);

    out << s.length() << L":" << s << L"\r\n";
    out << L"\r\n";
    out.close();
}
.

Nota: con UTF-16 è necessario utilizzare una modalità binaria anziché una modalità di testo per evitare la corruzione, quindi non possiamo utilizzare std::endl e devono utilizzare L"\r\n" per ottenere il comportamento del file di testo di fine linea end-line corretto. < / P >.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top