файл io — чтение / запись в Unicode Переполнение стека

К сожалению, это третий раз на этой неделе, я должен задать вопрос.

Я должен написать текст в файл с кодировкой Unicode (или UTF8).

Вот что я делаю:

создание wofstream mystream; а потом я положил wstring в этом так mystream << L"hello world";

Первый вопрос: какую кодировку использует поток в моем случае?

Во-вторых, я хочу загрузить свой новый файл, но как читать строки? ifstream«s getline не работает, потому что линия в конечном итоге разрушена, очевидно.

1

Решение

wchar_tтип, который поддерживает wstream а также wstring, зависит от платформы: 2 байта в Windows, 4 байта в некоторых (всех?) Linux. Таким образом, вы в конечном итоге напишите «Unicode», но именно то, какой Unicode подвержен многим переменным. Вы можете написать UTF32 / UCS4, вы можете в конечном итоге UTF16 / UCS2.

Если вы хотите писать, используя определенную, хорошо управляемую кодировку (например, UTF8 или UCS-2LE или UCS-2BE для управления порядком байтов), вам нужно что-то вроде Iconv. Вы также можете использовать std::locale в imbue поток, см. https://stackoverflow.com/a/1275260/105929.

1

Другие решения

Под «кодировкой Unicode» я полагаю, вы имеете в виду UTF-16. На самом деле существует несколько кодировок, которые можно назвать кодировками Unicode, но большинство людей, которые не знакомы с Unicode, воспринимают его как UTF-16 (я думаю, во многом потому, что Microsoft делает эту ошибку во всей своей документации). В моем ответе также предполагается, что вы пишете код для Windows и, следовательно, ваши внутренние данные хранятся в строках wchar_t в формате UTF-16.


Использование объекта с широким потоком не означает, что ввод или вывод файла будет выполняться с использованием широких символов. Фактически, широкий поток будет использовать фасет codecvt локали потока для преобразования между символьным типом потока (wchar_t) и char.

В C ++ 11 есть несколько аспектов codecvt, которые вы можете использовать для ввода / вывода UTF-16 или UTF-8; codecvt_utf8, codecvt_utf16, codecvt_utf8_utf16.

codecvt_utf8 преобразует между внешними многобайтовыми последовательностями UTF-8 и внутренними данными UTF-32 / UCS4 или UCS2. codecvt_utf16 преобразует между внешними многобайтовыми последовательностями UTF-16 и внутренними данными UTF-32 / UCS4 или UCS2. codecvt_utf8_utf16 преобразует между внешними многобайтовыми последовательностями UTF-8 и внутренними данными UTF-16.

Не существует встроенного способа преобразования между внешними многобайтовыми последовательностями UTF-16 и внутренними данными UTF-16, чего вы бы хотели, если бы внутренне использовались строки wchar_t в кодировке UTF-16 и внешние файлы в кодировке UTF-16.

Но так как вы указали, что вывод UTF-8 был приемлемым, фасет codecvt_utf8_utf16 будет работать хорошо.

#include <fstream>
#include <codecvt>

int main() {
std::wofstream mystream("test.txt");
mystream.imbue(std::locale(std::locale(),
new std::codecvt_utf8_utf16<wchar_t, 0x10ffff, std::codecvt_mode(std::consume_header|std::generate_header)>));
mystream << "Hello, World!\n";
}

Также обратите внимание, что этот пример устанавливает параметры для фасета codecvt_utf8_utf16 для генерации и чтения так называемой «спецификации UTF-8». Это соглашение Microsoft по предположению о кодировке файла и, как правило, не подходит для других платформ.


Следующее не имеет отношения к рассматриваемому вопросу, но управление аспектами времени жизни не похоже на большинство других современных методов управления временем жизни C ++.

Фасеты подсчитываются по ссылке, и когда последний языковой стандарт, имеющий определенный фасет, уничтожается, фасет удаляется, если только это не было специально отключено путем создания фасета с параметром refs, равным 1, Приведенный выше пример кода оставляет управление временем жизни локали и, следовательно, выглядит как утечка памяти. Код верен, однако. С точки зрения безопасности исключений, единственным кодом, который может потенциально выполняться между успешным выделением и владением выделенным объектом, принимаемым языковым стандартом, является выражение std::locale() который объявлен noexcept.

Другой вариант — использовать фасет, который не управляется локалью, и просто гарантировать, что он переживет локаль и все копии. Использовать фасет со статической продолжительностью хранения очень просто, но не забудьте указать, что локали не должны удалять фасет, установив для счетчика ссылок значение 1.

static std::codecvt_utf8_utf16<wchar_t, 0x10ffff, std::codecvt_mode(std::consume_header|std::generate_header)> mycodecvt(1);
mystream.imbue(std::locale(std::locale(), mycodecvt));

Если локаль существует только в течение короткого времени в определенной области, то вы можете использовать обычную локальную переменную. Это то же самое, что и выше, но без static, Просто убедитесь, что локаль (как и каждая копия) уничтожена, прежде чем фасет выходит из области видимости.

Это один раз, когда умный указатель не делает вещи лучше, потому что передача прав собственности на умный объект, не обращающий внимания, является сложной задачей. Необходимо выяснить, как вручную обрабатывать исключения, возникающие после того, как языковой стандарт получил фасет и, следовательно, стал владельцем, но до того, как умный указатель откажется от владения.

8

По вопросам рекламы [email protected]