Использование ifstream, когда имя файла содержит расширенные символы

Использование C++Builder XE5 (bcc32) в Windows 7.

Я пытаюсь открыть файл, имя которого содержит широкий символ. Фактическое имя файла, с которым я тестирую, — C:\bΛx\foo.txt. Не-ASCII-символ U+039B.

У меня это имя файла правильно сохранено в файле std::wstring. Тем не менее, пытаясь:

std::ifstream f( filename.c_str() );

не удается открыть файл.

Конечно, в стандартном C++ fopen принимает только char *. Однако реализация Dinkumware C++ RTL имеет перегрузку, допускающую wchar_t *. К сожалению, реализация этой перегрузки в ...\Embarcadero\RAD Studio\12.0\source\cpprtl\Source\dinkumware\source\fiopen.cpp не вызывает _wfopen. Вместо этого он использует wcstombs для преобразования строки в UTF-8, а затем вызывает fopen.

Проверяя источник на наличие fopen, он вызывает узкую версию базовой функции ___topen, которая в конечном итоге передает строку UTF-8 в CreateFile.

Когда я проверяю попытку открыть файл с помощью Sysinternals Process Monitor, он показывает, что он пытался открыть файл со строкой файла UTF-8, и операционная система отклонила это с результатом NAME COLLISION.

Если я открою файл с помощью _wfopen( filename.c_str(), L"r" ), тогда все будет хорошо, и я смогу прочитать файл с помощью функций ввода-вывода C, но, конечно, я не могу использовать C++ iostreams.

Есть ли способ использовать std::ifstream для открытия файла с U+039B или другими подобными символами в имени файла?

Обратите внимание, что использование std::wifstream также не работает (он по-прежнему пытается открыть версию имени файла UTF-8).


person M.M    schedule 15.10.2014    source источник
comment
Это явно ошибка в Dinkumware для Windows. Windows не поддерживает UTF-8 в большинстве своих API. Вы должны подать отчет авторам Dinkumware. И wchar_t* версия ifstream, и wifstream должны использовать исходное значение как есть с _wfopen(), без преобразования в UTF-8 и вызова fopen(). Это может работать на других платформах, но не на Windows.   -  person Remy Lebeau    schedule 16.10.2014
comment
Об этом сообщили Embarcadero 2 года назад, он до сих пор открыт. См. QC #111462.   -  person Remy Lebeau    schedule 16.10.2014
comment
@RemyLebeau источник Dinkum имеет параметр #ifdef, который заставит все вызовы проходить через _wfopen, но (насколько я мог видеть при беглом взгляде) он не может заставить fstream(char *) перейти к fopen, а также fstream(wchar_t *) перейти к _wfopen в той же сборке. . Я не уверен, поддерживает ли C++Builder попытки пересобрать упакованную версию Dinkum для настройки _wfopen?   -  person M.M    schedule 16.10.2014
comment
@RemyLebeau спасибо, в этом отчете есть более быстрый краткосрочный обходной путь, чем покупка книги Джосуттиса (хотя, очевидно, она далека от идеала)   -  person M.M    schedule 16.10.2014


Ответы (1)


Если я открою файл с помощью _wfopen( filename.c_str(), L"r" ), тогда все будет хорошо, и я смогу прочитать файл с помощью функций ввода-вывода C, но, конечно, я не могу использовать C++ iostreams.

Я не вижу этого "конечно". Ваша проблема сводится к созданию iostreams streambuf из файла FILE*. Говард Хиннант ответил здесь, что в Стандарте нет метода, но реализация класса, производного от streambuf, поверх FILE* довольно хороша. простой. Он даже упоминает некоторый код, который, по его мнению, может стать хорошей отправной точкой.

Обратите внимание, что это имеет смысл только для текстового файла. iostreams и бинарные файлы не уживаются; есть уровень кодирования символов, и ios_base::binary его не отключает.

person Ben Voigt    schedule 15.10.2014
comment
где я могу прочитать больше о слое кодирования символов? (до С++ 11) - person M.M; 16.10.2014
comment
Мне только что пришло в голову, что я могу прочитать весь файл в память через fread или аналогичный, а затем инициализировать sstream необработанными данными и скрестить пальцы - person M.M; 16.10.2014
comment
@MattMcNabb: Определенно стоит попробовать. - person Ben Voigt; 16.10.2014