Вопрос

Рассмотрим следующий код:

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

В GCC 4.8 в Linux (Ubuntu 14.04) файл test.txt содержит это:

6:€€

В Visual C ++ 2013 в Windows он содержит следующее:

4:€\x80

(Под '\x80' я подразумеваю единственный 8-битный символ 0x80).

Мне совершенно не удалось заставить ни один компилятор выводить символ , использующий std::wstring.

Два вопроса:

  • Что именно, по мнению компилятора Microsoft, он делает с char* буквально?Очевидно, что он что-то делает для его кодирования, но что именно, неясно.
  • Каков правильный способ переписать приведенный выше код, используя std::wstring и std::wofstream так что он выводит два персонажи?
Это было полезно?

Решение

Это происходит потому, что вы используете \u20ac который является символьным литералом Юникода в строке ASCII.

MSVC кодирует "\xe2\x82\xac\u20ac" как 0xe2, 0x82, 0xac, 0x80, это 4 узких символа.По сути, он кодирует \u20ac как 0x80, потому что он сопоставил символ евро со стандартным кодовая страница 1252

GCC преобразует литерал Юникода /u20ac к 3-байтовой последовательности UTF-8 0xe2, 0x82, 0xac таким образом, результирующая строка заканчивается следующим образом 0xe2, 0x82, 0xac, 0xe2, 0x82, 0xac.

Если вы используете std::wstring = L"\xe2\x82\xac\u20ac" он кодируется MSVC как 0xe2, 0x00, 0x82, 0x00, 0xac, 0x00, 0xac, 0x20 это 4 широких символа, но поскольку вы смешиваете созданный вручную UTF-8 с UTF-16, результирующая строка не имеет особого смысла.Если вы используете std::wstring = L"\u20ac\u20ac" как и следовало ожидать, вы получаете 2 символа Юникода в широкой строке.

Следующая проблема заключается в том, что ofstream и wofstream MSVC всегда записываются в ANSI / ASCII.Чтобы заставить его записать в UTF-8, вы должны использовать <codecvt> (ПО сравнению С 2010 годом или более поздней версией):

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

и написать UTF-16 (или, более конкретно, 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();
}

Примечание:С UTF-16 вы должны использовать двоичный режим, а не текстовый, чтобы избежать повреждения, поэтому мы не можем использовать std::endl и должны использовать L"\r\n" чтобы получить правильное поведение текстового файла в конце строки.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top