Pregunta

Considere el siguiente código:

#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();
}

En GCC 4.8 en Linux (Ubuntu 14.04), el archivo test.txt contiene esto:

6:€€

En Visual C++ 2013 en Windows, contiene esto:

4:€\x80

(Por '\x80' me refiero al carácter único de 8 bits 0x80).

No he podido lograr que ninguno de los compiladores genere un personaje usando std::wstring.

Dos preguntas:

  • ¿Qué cree exactamente el compilador de Microsoft que está haciendo con el char* ¿literal?Obviamente está haciendo algo para codificarlo, pero no está claro qué es.
  • ¿Cuál es la forma correcta de reescribir el código anterior usando std::wstring y std::wofstream para que produzca dos ¿caracteres?
¿Fue útil?

Solución

Esto se debe a que estás usando \u20ac que es un carácter literal Unicode en una cadena ASCII.

Codificaciones MSVC "\xe2\x82\xac\u20ac" como 0xe2, 0x82, 0xac, 0x80, que son 4 caracteres estrechos.Básicamente codifica \u20ac como 0x80 porque asignó el carácter del euro al estándar 1252 página de códigos

GCC está convirtiendo el literal Unicode /u20ac a la secuencia UTF-8 de 3 bytes 0xe2, 0x82, 0xac entonces la cadena resultante termina como 0xe2, 0x82, 0xac, 0xe2, 0x82, 0xac.

Si utiliza std::wstring = L"\xe2\x82\xac\u20ac" MSVC lo codifica como 0xe2, 0x00, 0x82, 0x00, 0xac, 0x00, 0xac, 0x20 que tiene 4 caracteres de ancho, pero como estás mezclando un UTF-8 creado a mano con un UTF-16, la cadena resultante no tiene mucho sentido.Si usas un std::wstring = L"\u20ac\u20ac" obtienes 2 caracteres Unicode en una cadena ancha como era de esperar.

El siguiente problema es que ofstream y wofstream de MSVC siempre escriben en ANSI/ASCII.Para que escriba en UTF-8 debes usar <codecvt> (VS 2010 o posterior):

#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();
}

y escribir UTF-16 (o más específicamente 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 tienes que usar un modo binario en lugar de modo texto para evitar la corrupción, por lo que no podemos usar std::endl y tengo que usar L"\r\n" para obtener el comportamiento correcto del archivo de texto de final de línea.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top