Я хочу читать / записывать файл с именем файла в юникоде, используя файловую систему boost, языковой стандарт boost в Windows (mingw) (в конце должен быть независимым от платформы).
Это мой код:
#include <boost/locale.hpp>
#define BOOST_NO_CXX11_SCOPED_ENUMS
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
namespace fs = boost::filesystem;
#include <string>
#include <iostream>
int main() {
std::locale::global(boost::locale::generator().generate(""));
fs::path::imbue(std::locale());
fs::path file("äöü.txt");
if (!fs::exists(file)) {
std::cout << "File does not exist" << std::endl;
}
fs::ofstream(file, std::ios_base::app) << "Test" << std::endl;
}
fs::exists
действительно проверяет файл с именем äöü.txt
,
Но записанный файл имеет название äöü.txt
,
Чтение дает ту же проблему. С помощью fs::wofstream
тоже не помогает, так как это просто обрабатывает широкий ввод.
Как я могу это исправить с помощью C ++ 11 и повысить?
Редактировать: Сообщение об ошибке опубликовано: https://svn.boost.org/trac/boost/ticket/9968
Чтобы уточнить для награды: С Qt все довольно просто, но я бы хотел кроссплатформенное решение, использующее только C ++ 11 и Boost, без Qt и ICU.
Это может быть сложно по двум причинам:
В исходном файле C ++ есть не-ASCII-строка. Как этот литерал преобразуется в двоичное представление const char *
будет зависеть от настроек компилятора и / или настроек кодовой страницы ОС.
Windows работает только с именами файлов Unicode через кодировку UTF-16, в то время как Unix использует UTF-8 для имен файлов Unicode.
Чтобы это работало в Windows, вы можете попытаться изменить свой литерал на широкие символы (UTF-16):
const wchar_t *name = L"\u00E4\u00F6\u00FC.txt";
fs::path file(name);
Чтобы получить полное кроссплатформенное решение, вам нужно начать со строки UTF-8 или UTF-16, а затем убедиться, что она правильно преобразована в path::string_type
учебный класс.
К сожалению, C ++ (и, следовательно, Boost) ofstream
API не позволяет указывать wchar_t
строки в качестве имени файла. Это касается как конструктор и open
метод.
Вы можете убедиться, что объект пути не будет немедленно преобразован в const char *
(используя строковый API C ++ 11), но это, вероятно, не поможет:
std::ofstream(file.native()) << "Test" << std::endl;
Чтобы Windows работала, вам, возможно, придется вызывать API-интерфейс Windows с поддержкой Unicode, CreateFileW
, преобразовать HANDLE
к FILE *
затем используйте FILE *
для ofstream
конструктор. Это все описано в другом ответе StackOverflow, но я не уверен, что это ofstream
конструктор будет существовать на MinGW.
к несчастью basic_ofstream
похоже, не позволяет создавать подклассы для пользовательских basic_filebuf
типы, поэтому FILE *
преобразование может быть единственным (полностью непереносимым) вариантом.
Вместо того, чтобы использовать файловые потоки, вы также можете записывать в файлы, используя ввод-вывод с отображением в память. В зависимости от того, как Boost реализует это (это не входит в стандартную библиотеку C ++), этот метод может работать с именами файлов Windows Unicode.
Вот пример повышения (взят из другой ответ) который использует path
Объект для открытия файла:
#include <boost/filesystem.hpp>
#include <boost/iostreams/device/mapped_file.hpp>
#include <iostream>
int main()
{
boost::filesystem::path p(L"b.cpp");
boost::iostreams::mapped_file file(p); // or mapped_file_source
std::cout << file.data() << std::endl;
}
Я не знаю, как ответ здесь был принят, так как ОП fs::path::imbue(std::locale());
точно не наплевать на кодовую страницу ОС, std::wstring
и что «нет. В противном случае, да, он просто использовал бы старый добрый iconv, звонки Winapi или другие вещи, предложенные в принятом ответе. Но это не смысл использования boost :: locale Вот.
Реальный ответ, почему это не работает, хотя ОП делает imbue()
текущая локаль, как указано в документации Boost (см. «Кодировка по умолчанию под Microsoft Windows»), из-за багов (или ошибок mingw), которые остаются нерешенными в течение по крайней мере пары лет с марта 2015 года.
К сожалению, пользователи mingw, похоже, остались в дураках.
Теперь то, что разработчики поддержки должны сделать, чтобы покрыть эти ошибки, совсем другое дело. Может оказаться, что они должны точно реализовать то, что сказал Дэн.
Рассматривали ли вы подход использования символов ASCII в исходном коде и использования возможностей форматирования Boost-сообщений библиотеки Boost.Locale для поиска нужной строки с помощью ключа ASCII?
http://www.boost.org/doc/libs/1_55_0/libs/locale/doc/html/messages_formatting.html
В качестве альтернативы вы можете использовать библиотеку Boost.Locale, чтобы сгенерировать библиотеку UTF-8, а затем наполнить Boost.Path этой локалью, используя «boost :: path :: imbue ().»http://boost.2283326.n4.nabble.com/boost-filesystem-path-as-utf-8-td4320098.html
Это также может быть полезно для вас.
Кодировка по умолчанию под Microsoft Windows
http://www.boost.org/doc/libs/1_51_0/libs/locale/doc/html/default_encoding_under_windows.html
РЕДАКТИРОВАТЬ: добавить ссылки на boost и wchar_t в конце поста и другое возможное решение для Windows
Я мог бы воспроизвести почти то же самое на Ubuntu и на Windows, даже не используя boost (у меня его нет на моей коробке с windows). Чтобы это исправить, мне просто пришлось преобразовать исходный код в ту же кодировку, что и система, то есть utf8 в Ubuntu и latin1 или iso-8859-1 в Windows.
Как я и подозревал, проблема исходит от линии fs::path file("äöü.txt");
, Поскольку кодировка файла не соответствует ожидаемой, она более или менее читается как fs::path file("äöü.txt");
, Если вы контролируете, вы обнаружите, что размер равен 10. Это полностью объясняет, что выходной файл имеет неправильное имя.
Я подозреваю, что тест if (!fs::exists(file))
правильно работает, потому что Boost или Windows автоматически исправляет кодировку на входе.
Поэтому в Windows просто используйте редактор в кодовой странице 1252 или в латинице 1 или iso-8859-1, и у вас не должно быть проблем при условии, что вам не нужно использовать символы вне этой кодировки. Если вам нужны символы за пределами Latin1, я боюсь, что вам придется использовать Unicode API Windows.
РЕДАКТИРОВАТЬ:
Фактически, Windows (> NT) изначально работает с wchar_t
и не char
, И не удивительно, что Boost на Windows делает то же самое — смотрите увеличить библиотечную файловую систему.
Извлечь:
Для Windows-подобных реализаций, включая MinGW, path :: value_type имеет вид
wchar_t. Пропущенный языковой стандарт по умолчанию обеспечивает фасет codecvt, который
вызывает Windows MultiByteToWideChar или WideCharToMultiByte API с
кодовая страница CP_THREAD_ACP, если Windows AreFileApisANSI () имеет значение true …
Таким образом, другое решение в Windows, которое позволило бы полный набор символов Юникода (или, по крайней мере, подмножество, предлагаемое Windows), состояло бы в том, чтобы указать путь к файлу как wstring
а не как string
, В качестве альтернативы, если вы действительно хотите использовать имена файлов в кодировке UTF8, вы должны будете принудительно заставить языковой стандарт потока использовать UTF8, а не CP1252. Я не могу привести пример кода этого, потому что у меня нет надстройки на моем окне Windows, мой ящик Windows работает на старой XP и не поддерживает UTF8, и я не хочу публиковать непроверенный код, но я думаю, что в этом случае вы следует заменить
std::locale::global(boost::locale::generator().generate(""));
с чем-то вроде:
std::locale::global(boost::locale::generator().generate("UTF8"));
ВНИМАНИЕ: не проверено, поэтому я не уверен, является ли строка для генерирования UTF8 или что-то еще …