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

时间:2014-04-30 16:53:55

标签: c++ boost unicode 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;
}

fs::exists确实检查名为äöü.txt的文件。 但是书面文件的名称为äöü.txt

阅读给出了同样的问题。使用fs::wofstream也无济于事,因为这只会处理广泛的输入。

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

修改:已发布错误报告:https://svn.boost.org/trac/boost/ticket/9968

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

4 个答案:

答案 0 :(得分:9)

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

  1. C ++源文件中有非ASCII字符串。该文字如何转换为const char *的二进制表示将取决于编译器设置和/或操作系统代码页设置。

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

  3. 构造路径对象

    要在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字符串指定为文件名。 constructoropen 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或其他...