Pergunta

Considere o seguinte 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();
}

No GCC 4.8 no Linux (Ubuntu 14.04), o arquivo test.txt contém isto:

6:€€

No Visual C++ 2013 no Windows, ele contém isto:

4:€\x80

(Por '\x80' quero dizer o caractere único de 8 bits 0x80).

Não consegui fazer com que nenhum dos compiladores produzisse um personagem usando std::wstring.

Duas questões:

  • O que exatamente o compilador da Microsoft pensa que está fazendo com o char* literal?Obviamente está fazendo algo para codificá-lo, mas o que não está claro.
  • Qual é a maneira correta de reescrever o código acima usando std::wstring e std::wofstream para que ele produza dois personagens?
Foi útil?

Solução

Isso ocorre porque você está usando \u20ac que é um caractere literal Unicode em uma string ASCII.

Codificações MSVC "\xe2\x82\xac\u20ac" como 0xe2, 0x82, 0xac, 0x80, que tem 4 caracteres estreitos.Essencialmente codifica \u20ac como 0x80 porque mapeou o caractere euro para o padrão Página de código 1252

GCC está convertendo o literal Unicode /u20ac para a sequência UTF-8 de 3 bytes 0xe2, 0x82, 0xac então a string resultante termina como 0xe2, 0x82, 0xac, 0xe2, 0x82, 0xac.

Se você usar std::wstring = L"\xe2\x82\xac\u20ac" ele é codificado por MSVC como 0xe2, 0x00, 0x82, 0x00, 0xac, 0x00, 0xac, 0x20 que tem 4 caracteres largos, mas como você está misturando um UTF-8 criado manualmente com um UTF-16, a string resultante não faz muito sentido.Se você usar um std::wstring = L"\u20ac\u20ac" você obtém 2 caracteres Unicode em uma string larga, como seria de esperar.

O próximo problema é que ofstream e wofstream do MSVC sempre escrevem em ANSI/ASCII.Para escrever em UTF-8 você deve usar <codecvt> (VS 2010 ou 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();
}

e escrever UTF-16 (ou mais especificamente 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();
}

Observação:Com UTF-16 você tem que usar um modo binário em vez de modo texto para evitar corrupção, então não podemos usar std::endl e tem que usar L"\r\n" para obter o comportamento correto do arquivo de texto de fim de linha.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top