我想使用boost文件系统读取/写入带有unicode文件名的文件,在Windows上使用boost语言环境(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和boost来解决这个问题?
修改:已发布错误报告:https://svn.boost.org/trac/boost/ticket/9968
澄清赏金: Qt非常简单,但我想要一个只使用C ++ 11和Boost,没有Qt而没有ICU的跨平台解决方案。
答案 0 :(得分:9)
这可能很复杂,原因有两个:
C ++源文件中有非ASCII字符串。该文字如何转换为const char *
的二进制表示将取决于编译器设置和/或操作系统代码页设置。
Windows仅通过UTF-16编码使用Unicode文件名,而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
类。< / p>
不幸的是,C ++(以及Boost)ofstream
API不允许将wchar_t
字符串指定为文件名。 constructor和open
method都属于这种情况。
您可以尝试确保路径对象不会立即转换为const char *
(通过使用C ++ 11字符串API),但这可能不会有帮助:
std::ofstream(file.native()) << "Test" << std::endl;
要使Windows正常工作,您可能必须调用支持Unicode的Windows API CreateFileW
,将HANDLE
转换为FILE *
,然后使用FILE *
} ofstream
构造函数。这都是described in another StackOverflow answer,但我不确定MinGW上是否存在ofstream
构造函数。
不幸的是basic_ofstream
似乎不允许自定义basic_filebuf
类型的子类化,因此FILE *
转换可能是唯一的(完全不可移植)选项。
您也可以使用memory-mapped I/O写入文件,而不是使用文件流。根据Boost如何实现这一点(它不是C ++标准库的一部分),此方法可以使用Windows Unicode文件名。
这是一个使用path
对象打开文件的提升示例(取自another answer):
#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;
}
答案 1 :(得分:4)
我不知道这里的答案是如何被接受的,因为OP确实fs::path::imbue(std::locale());
正好而不是对操作系统的代码页,{{1}什么不是。否则,他只是使用普通的iconv,Winapi电话或接受的答案中建议的其他内容。但是不是在这里使用boost :: locale 。
真正的答案为什么这不起作用,即使OP std::wstring
当前的语言环境,如Boost的文档中指示的那样(请参阅"Default Encoding under Microsoft Windows" ),是因为截至2015年3月至少几年未解决的问题(或mingw)。
不幸的是,mingw用户似乎被冷落了。
现在,为了弥补这些错误,开发人员应该采取的措施是完全不同的事情。事实证明,他们需要准确地实施丹所说的。
答案 2 :(得分:2)
您是否考虑过在源代码中使用ASCII字符并使用Boost.Locale库的Boost Messages Formatting功能使用ASCII密钥查找所需字符串的方法? http://www.boost.org/doc/libs/1_55_0/libs/locale/doc/html/messages_formatting.html
或者,您可以使用Boost.Locale库生成UTF-8库,然后使用&#34;使用该语言环境填充Boost.Path。升压::路径::灌输()&#34。 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
答案 3 :(得分:1)
编辑:在帖子末尾添加对boost和wchar_t的引用,在Windows上添加另一个可能的解决方案
我甚至可以在ubuntu和Windows上重现几乎相同的东西,甚至没有使用提升(我不会在我的Windows框上有它)。为了解决这个问题,我只需要使用与系统相同的编码来转换源代码,即在Ubuntu上使用utf8,在Windows上使用latin1或iso-8859-1。
我怀疑,问题来自fs::path file("äöü.txt");
行。由于文件的编码不是预期的,因此它或多或少地被读作fs::path file("äöü.txt");
。你控制它,你会发现大小是10.这完全解释了输出文件的名称错误。
我怀疑测试if (!fs::exists(file))
正确有效,因为boost或windows会自动修复输入的编码。
所以在Windows上,只需使用代码页1252或latin1或iso-8859-1中的编辑器,如果您不必使用此字符集之外的字符,则不应该有问题。如果您需要Latin1之外的字符,我担心您将不得不使用Windows的unicode API。
编辑:
事实上,Windows(&gt; NT)本身与wchar_t
合作,而不是char
。毫不奇怪,Windows上的提升也是如此 - 请参阅boost library filesystemreference。
提取:
对于类似Windows的实现,包括MinGW,path :: value_type是 wchar_t的。默认的imbued语言环境提供了一个codecvt方面 使用a调用Windows MultiByteToWideChar或WideCharToMultiByte API 如果Windows AreFileApisANSI()为true,则为CP_THREAD_ACP的代码页...
因此,Windows中允许完整unicode字符集(或至少是Windows原生提供的子集)的另一个解决方案是将文件路径设为wstring
而不是string
。 。或者,如果您确实想使用UTF8编码的文件名,则必须强制线程区域设置使用UTF8而不是CP1252。我不能给出代码示例,因为我的窗口框没有增强,我的Windows框运行旧版XP并且不支持UTF8而且我不想发布未经测试的代码,但我认为那种情况下,你应该替换
std::locale::global(boost::locale::generator().generate(""));
有类似的东西:
std::locale::global(boost::locale::generator().generate("UTF8"));
当心:未经测试,因此我不确定生成的字符串是否为UTF8或其他...