проблема с использованием getline с файлом юникода

ОБНОВЛЕНИЕ: спасибо @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

Например, если мой файл 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 (или читается до конца файла, чего я и хочу)?
Спасибо.


person hamishmcn    schedule 27.04.2010    source источник
comment
Многобайтовый файл должен читаться как '\n', не так ли? То же самое для версии с широкими символами - она ​​должна читаться до новой строки с широкими символами.   -  person Jonathan Leffler    schedule 28.04.2010
comment
UCS-2 не должен выполнять никаких преобразований, поэтому он не может быть конечным автоматом. Это работает правильно, если у вас нет \0 s?   -  person Potatoswatter    schedule 28.04.2010
comment
@Jonathan Leffler файл MB читается в EOF (включая \n), потому что я сказал ему, что разделитель, на котором нужно остановиться, равен 0   -  person hamishmcn    schedule 28.04.2010
comment
@Potatoswatter тот же результат для результата широкоугольной версии, если я удалю whar_t (0) - он перестанет читать на первом 0 байте.   -  person hamishmcn    schedule 28.04.2010
comment
@hamish: нет, я имею в виду, что произойдет, если в файле нет 0 байтов. Кроме того, не останавливается ли чтение на первом 0 байте, о чем вы просили? Было бы полезно, если бы вы были более конкретными. Это действует так, как будто 0 байт - это конец файла? Это не то, на что похоже ваше нынешнее описание.   -  person Potatoswatter    schedule 28.04.2010
comment
@Potatoswatter Исправление (я был пойман отладчиком, который не показывал мне строку правильно), если я удалю wchar_t (0), то он читается до конца первой строки, как и следовало ожидать.   -  person hamishmcn    schedule 28.04.2010


Ответы (3)


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

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

EDIT: Вы установили язык?

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

or

locale::global( encoding_support::ucs2_bigendian_encoding );

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

person Potatoswatter    schedule 28.04.2010
comment
Нет, я не устанавливал локаль - я буду экспериментировать с ней (но я не понимаю, почему это должно измениться, читает ли getline 1 байт или 2 байта, когда параметры wchar_ts). Я не понимаю, что вы имеете в виду, когда говорите, что encoding_support — это какая-то библиотека — все, что я хочу сделать, это прочитать файл в wchar_ts? - person hamishmcn; 28.04.2010
comment
Potatoswatter Я застрял, не знаю, что поставить в качестве параметра локали. Я предполагаю, что вы хотите, чтобы я заменил что-то, если ваша система поддерживает UCS-2, с другим параметром, но я не знаю, что. Должно ли это быть название языка? Конечно, смысл использования широких символов заключается в том, чтобы избежать установки кодовых страниц и тому подобного? Я использую WinXP SP3. Конечно, С++ может читать wchar_ts из файла без указания языка? - person hamishmcn; 28.04.2010
comment
@hamish: Хотел бы я знать, что тебе сказать. Я бегло просмотрел документацию MSDN, но она полностью сосредоточена на интернационализации языка, а не на кодировании данных. Попробуйте расширить поиск, включив в него UTF-16 и сторонние библиотеки… или рассмотрите возможность отказа от стандартной библиотеки и прочтения файла самостоятельно. Возможно, вы можете связаться с разработчиком программного обеспечения, создавшего файл, который вы пытаетесь прочитать, и спросить, как они это сделали. - person Potatoswatter; 28.04.2010
comment
Я ценю все ваши комментарии и время, которое вы вложили в это. Для записи - это не большая проблема для меня (я создатель файла, который я пытаюсь прочитать, и я могу прочитать его, прочитав массив символов), это просто меня беспокоит что это не работает так, как я ожидал, - мне нужен был быстрый и простой способ С++ (с std::strings и ifstreams), чтобы сделать это :-) - person hamishmcn; 28.04.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()-записи в потоке, либо путем изменения глобальной), то в вашем случае, вероятно, произойдет то, что chars будут прочитаны из файла и преобразованы в wchar_t один с помощью один. Таким образом, сначала читается FF, затем FE, затем 00, и getline(..., 0) тут же останавливается.

person Éric Malenfant    schedule 28.04.2010
comment
Это согласуется с тем, что я увидел, когда скопировал код для getline и заменил шаблонные типы типами, которые я использую, чтобы я мог пройтись и попытаться выяснить, что происходит - wifstream считывал по одному байту за раз в мой wchar_t - person hamishmcn; 29.04.2010

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

person Windows programmer    schedule 28.04.2010
comment
Lucs2-be.txt — это просто имя файла. FE FF имеет обратный порядок байтов. - person Potatoswatter; 28.04.2010
comment
Вы правы, имя файла предназначено для того, чтобы ввести читателей в заблуждение, но не имеет никакого влияния на машины. Но что-то все еще не так. FE FF имеет порядок байтов с прямым порядком байтов, но все, что следует за ним, имеет порядок байтов с прямым порядком байтов. - person Windows programmer; 28.04.2010
comment
Все остальное тоже с обратным порядком байтов. Нули идут впереди, а значащая часть после. - person Potatoswatter; 28.04.2010
comment
Вы знаете, что делают люди, когда так сильно облажались, как я здесь. Они удаляют свои ответы :-) - person Windows programmer; 28.04.2010
comment
Спасибо - последний комментарий рассмешил :-) - person hamishmcn; 28.04.2010