Question

Considérez le code suivant :

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

Sous GCC 4.8 sous Linux (Ubuntu 14.04), le fichier test.txt contient ceci :

6:€€

Sous Visual C++ 2013 sous Windows, il contient ceci :

4:€\x80

(Par '\x80', j'entends le caractère unique de 8 bits 0x80).

J'ai été complètement incapable de faire en sorte que l'un ou l'autre des compilateurs génère un personnage utilisant std::wstring.

Deux questions:

  • Que pense exactement le compilateur Microsoft avec le char* littéral?Il fait évidemment quelque chose pour l'encoder, mais ce n'est pas clair.
  • Quelle est la bonne façon de réécrire le code ci-dessus en utilisant std::wstring et std::wofstream pour qu'il produise deux personnages?
Était-ce utile?

La solution

C'est parce que vous utilisez \u20ac qui est un caractère littéral Unicode dans une chaîne ASCII.

Encodages MSVC "\xe2\x82\xac\u20ac" comme 0xe2, 0x82, 0xac, 0x80, qui est de 4 caractères étroits.Il code essentiellement \u20ac comme 0x80 car il mappait le caractère euro au standard 1252 page de code

GCC convertit le littéral Unicode /u20ac à la séquence UTF-8 de 3 octets 0xe2, 0x82, 0xac donc la chaîne résultante se termine par 0xe2, 0x82, 0xac, 0xe2, 0x82, 0xac.

Si tu utilises std::wstring = L"\xe2\x82\xac\u20ac" il est codé par MSVC comme 0xe2, 0x00, 0x82, 0x00, 0xac, 0x00, 0xac, 0x20 qui contient 4 caractères larges, mais comme vous mélangez un UTF-8 créé à la main avec un UTF-16, la chaîne résultante n'a pas beaucoup de sens.Si vous utilisez un std::wstring = L"\u20ac\u20ac" vous obtenez 2 caractères Unicode dans une chaîne large comme vous vous en doutez.

Le problème suivant est que les ofstream et wofstream de MSVC écrivent toujours en ANSI/ASCII.Pour le faire écrire en UTF-8, vous devez utiliser <codecvt> (VS 2010 ou version ultérieure) :

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

et pour écrire UTF-16 (ou plus précisément 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();
}

Note:Avec UTF-16, vous devez utiliser un mode binaire plutôt que le mode texte pour éviter toute corruption, nous ne pouvons donc pas utiliser std::endl et je dois utiliser L"\r\n" pour obtenir le comportement correct du fichier texte de fin de ligne.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top