Я компилирую свою библиотеку с поддержкой unicode, и теперь у меня возникла проблема в том, что мне нужно открыть файл данных, который находится в ASCII, но, конечно, имя файла имеет широкие диалоги. Итак, как бы я это сделал?
Когда я использую этот код:
std::wstring s = L"path";
std::ifstream fl;
fl.open(s.c_str(), std::fstream::in);
то я получаю эту ошибку:
error: no matching function for call to 'std::basic_ifstream<char>::open(const wchar_t*, const openmode&)'
И когда я использую
std::wifstream fl;
fl.open(s.c_str(), std::fstream::in);
Я получаю ту же ошибку (???)
error: no matching function for call to 'std::basic_ifstream<wchar_t>::open(const wchar_t*, const openmode&)'
Тем не менее, когда я буду использовать std::wifstream
, std::wifstream
ли поток входные данные в unicode? Если это так, тогда мне придется использовать std::ifstream
но мне все равно нужно передать ему широкую строку или каким-то образом преобразовать ее в ASCII. Каков правильный способ сделать это?
Я использую MingW gcc 4.8.3.
mingw32-g++.exe -pedantic -std=c++11 -Wextra -Wall -g -D_UNICODE -D_WIN32 -Iinclude -c propertyfile.cpp -o propertyfile.o
Что-то вроде этого:
std::wstring s = L"path";
std::string ansi( begin(s), end(s) );
std::ifstream fl ( ansi );
Если файл ASCII, вы не можете использовать wifstream.
Заметьте, что он работает только в том случае, если путь является чистым ASCII.
Для решения этой проблемы нет простого решения. У меня есть две идеи для этого:
_wfopen
.wchar
в Windows), а имена файлов Unix указываются в байтах ( char
, обычно интерпретируемый как UTF-8 в современном Linux / OSX). Вы должны переключаться между байтово-ориентированными вызовами стандартной библиотеки C и специфичными для Windows вызовами (например, _wfopen
или прямыми эквивалентами Win32 API, такими как OpenFileW
) в зависимости от платформы, на которой вы работаете, что печально. (Или, как уже упоминалось, используйте библиотеку, которая делает это за вас.)
std::ifstream
- это просто std::istream
с простым способом установить файл в качестве цели. Поскольку он не позволяет UTF-8, вы должны установить файл по-другому. Если посмотреть на конструкторы std::streambuf
вы должны заметить std::streambuf
(который также можно установить/заменить rdbuf). Теперь вы не могли бы написать свой собственный streambuf производного класса, который использует _wfopen, но нет необходимости: MinGW имеет расширение, называемое __gnu_cxx::stdio_filebuf
(#include <ext/stdio_filebuf.h>
).
Из источника:
/**
* @param __fd An open file descriptor.
* @param __mode Same meaning as in a standard filebuf.
* @param __size Optimal or preferred size of internal buffer,
* in chars.
*
* This constructor associates a file stream buffer with an open
* POSIX file descriptor. The file descriptor will be automatically
* closed when the stdio_filebuf is closed/destroyed.
*/
stdio_filebuf(int __fd, std::ios_base::openmode __mode,
size_t __size = static_cast<size_t>(BUFSIZ));
/**
* @param __f An open @c FILE*.
* @param __mode Same meaning as in a standard filebuf.
* @param __size Optimal or preferred size of internal buffer,
* in chars. Defaults to system @c BUFSIZ.
*
* This constructor associates a file stream buffer with an open
* C @c FILE*. The @c FILE* will not be automatically closed when the
* stdio_filebuf is closed/destroyed.
*/
stdio_filebuf(std::__c_file* __f, std::ios_base::openmode __mode,
size_t __size = static_cast<size_t>(BUFSIZ));
Ваша единственная работа - перевести std::ios_base::openmode
в режим для файлового дескриптора или указателя файла. FILE*
не передает свое владение, поэтому вам все равно придется заботиться об этом, но std::unique_ptr
с функцией закрытия файла как deleter, связанной каким-либо образом с вашим stdio_filebuf
, тоже решает.
MSVC поддерживает имена файлов Unicode с помощью std::wifstream
(utf-8 для преобразования utf-16 с помощью MultiByteToWideChar), Clang совместим с MinGW.
Написание кросс-платформенного класса fstream теперь легко. Прежде всего: вывод из std::fstream
. В Unix ваш класс должен выглядеть как std::fstream
, поэтому вам просто нужно скопировать конструкторы (С++ 11 way: using std::fstream::fstream;
). В Windows вам нужно реализовать конструкторы (которые просто вызывают open
) и переопределить открытые функции-члены (в конце все просто переходит к одной открытой функции, где лежит работа, не более того).
Попробуй это:
fl.open(s.c_str(), std::ios_base::in);
Я не уверен в mingw32-g++, но вы должны использовать ios_base
в компиляторе MS. Что касается данных, которые он ожидает - да, он будет ожидать unicode или ansi в зависимости от типа, но только если вы прочитаете его как текст. Прочитайте его как двоичные данные, а затем просто скопируйте в свой массив ansi, и вы получите ansi-текст из файла Unicode.