Iderali Unicode in Visual C ++
-
26-12-2019 - |
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
estd::wofstream
in modo che tu possa emetterà due caratteri€
?
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 >.