使用unicode文件名读取/写入带有普通C++/Boost的文件

Mik*_*e M 10 c++ unicode boost boost-filesystem boost-locale

我想使用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;
}
Run Code Online (Sandbox Code Playgroud)

fs::exists真正检查与名称的文件äöü.txt.但是书面文件有名字äöü.txt.

阅读给出了同样的问题.使用fs::wofstream也没有帮助,因为这只是处理广泛的输入.

如何使用C++ 11和boost来解决这个问题?

编辑:发布错误报告:https://svn.boost.org/trac/boost/ticket/9968

澄清赏金: Qt很简单,但我想要一个只使用C++ 11和Boost,没有Qt和没有ICU的跨平台解决方案.

Dan*_*ile 9

这可能很复杂,原因有两个:

  1. C++源文件中有一个非ASCII字符串.此文字如何转换为a的二进制表示const char *将取决于编译器设置和/或OS代码页设置.

  2. Windows仅通过UTF-16编码使用Unicode文件名,而Unix使用UTF-8作为Unicode文件名.

构造路径对象

要在Windows上使用此功能,您可以尝试将文字更改为宽字符(UTF-16):

const wchar_t *name = L"\u00E4\u00F6\u00FC.txt";
fs::path file(name);
Run Code Online (Sandbox Code Playgroud)

要获得完整的跨平台解决方案,您必须从UTF-8或UTF-16字符串开始,然后确保将其正确转换为path::string_type类.

打开文件流

不幸的是,C++(以及Boost)ofstreamAPI不允许将wchar_t字符串指定为文件名.构造函数open方法都是这种情况.

您可以尝试确保路径对象不会立即转换为const char *(通过使用C++ 11字符串API),但这可能无济于事:

std::ofstream(file.native()) << "Test" << std::endl;
Run Code Online (Sandbox Code Playgroud)

对于Windows工作,你也许能纷纷致电支持Unicode的Windows API的,CreateFileW,转换HANDLE到一个FILE *,然后使用FILE *ofstream构造函数.这是在另一个StackOverflow答案中描述的,但我不确定ofstreamMinGW上是否存在该构造函数.

遗憾的是basic_ofstream,似乎不允许为自定义basic_filebuf类型进行子类化,因此FILE *转换可能是唯一的(完全不可移植的)选项.

另一种方法:内存映射文件

您也可以使用内存映射I/O写入文件,而不是使用文件流.根据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;
}
Run Code Online (Sandbox Code Playgroud)