проблема с использованием getline с файлом Unicode - PullRequest
2 голосов
/ 28 апреля 2010

ОБНОВЛЕНИЕ: Спасибо @Potatoswatter и @Jonathan Leffler за комментарии - довольно смущающе, я был пойман подсказкой отладчика, не показывающей значение wstring правильно - однако это все еще не совсем работает для меня, и я имею обновил вопрос ниже:

Если у меня небольшой многобайтовый файл, который я хочу прочитать в строку, я использую следующий прием - я использую getline с разделителем '\0', например,

std::string contents_utf8;
std::ifstream inf1("utf8.txt");
getline(inf1, contents_utf8, '\0');

Это читает весь файл, включая переводы строки.
Однако, если я пытаюсь сделать то же самое с файлом широких символов, это не сработает - мой wstring читает только до первой строки.

std::wstring contents_wide;
std::wifstream inf2(L"ucs2-be.txt");
getline( inf2, contents_wide, wchar_t(0) ); //doesn't work

Например, файл my if unicode содержит символы A и B, разделенные CRLF, гекс выглядит следующим образом:

FE FF 00 41 00 0D 00 0A 00 42

Основываясь на том факте, что с многобайтовым файлом getline с '\ 0' читает весь файл, я полагал, что getline( inf2, contents_wide, wchar_t(0) ) следует читать во всем файле Unicode. Однако это не так - в приведенном выше примере моя широкая строка будет содержать следующие два wchar_ts: FF FF

(Если я удаляю wchar_t (0), он читается в первой строке, как и ожидалось (т.е. FE FF 00 41 00 0D 00)

Почему wchar_t (0) не работает как разделитель wchar_t, так что getline останавливается на 00 00 (или читает до конца файла, что я и хочу)?
Спасибо

Ответы [ 3 ]

2 голосов
/ 28 апреля 2010

Ваш UCS-2 декодер работает неправильно. Результат getline( inf2, contents_wide ) на FE FF 00 41 00 0D 00 0A 00 42 должен быть 0041 0000 = L"A". Предполагая, что вы находитесь в Windows, конец строки должен быть правильно преобразован, а метка порядка байтов не должна отображаться в выходных данных.

Предложите еще раз проверить документацию вашей ОС относительно того, как вы установили локаль.

РЕДАКТИРОВАТЬ: Вы установили язык?

locale::global( locale( "something if your system supports UCS-2" ) );

или

locale::global( encoding_support::ucs2_bigendian_encoding );

где encoding_support - некоторая библиотека.

1 голос
/ 28 апреля 2010

См. Этот вопрос: Почему широкий файловый поток в C ++ сужает записанные данные по умолчанию? , где автор удивляется конверсии wchar_t -> char при записи.

Ответы на этот вопрос применимы и к делу о чтении. В двух словах: на самом низком уровне файловый ввод / вывод всегда выполняется в байтах. basic_filebuf (что fstream использует для фактического выполнения ввода / вывода) использует фасет codecvt для преобразования между "внутренней" кодировкой (тип char, видимый программой и используемый для создания экземпляра потока wchar_t в вашем случае) и "внешняя" кодировка файла (которая всегда char).

codecvt получается из потока locale. Если в потоке нет языкового стандарта imbue() -d, используется глобальный языковой стандарт. По умолчанию глобальной локалью является «классическая» (или «C») локаль. codecvt аспект этой локали довольно простой. Я не знаю, что говорит стандарт об этом, но, по моему опыту в Windows, он просто «бросает» между char и wchar_t, один за другим. В Linux это тоже происходит, но происходит сбой, если значение символа находится вне диапазона ASCII.

Итак, если вы не трогаете языковой стандарт (либо imbue(), то есть один в потоке или изменяющий глобальный), то, вероятно, в вашем случае случается, что char s читаются из файла и приведение к wchar_t один за другим . Таким образом, сначала читается FF, затем FE, затем 00, и getline(..., 0) сразу останавливается.

0 голосов
/ 28 апреля 2010

L "ucs2-be.txt" выглядит для меня как флаг с прямым порядком байтов, но массив FE FF 00 41 00 0D 00 0A 00 42 выглядит как байтовый порядок байтов. Я думаю, именно поэтому символ FE FF был прочитан в ваш массив, а не пропущен. Я не могу понять, почему наличие или отсутствие wchar (0) влияет на результаты.

...