【问题标题】:UTF-8 names with boost::filestream under MinGWMinGW 下带有 boost::filestream 的 UTF-8 名称
【发布时间】:2018-03-05 22:32:24
【问题描述】:

我遇到了boost文件流的问题:我需要在windows下的用户目录中创建和修改文件。但是,用户名包含一个变音符号,这在 MinGW 下编译时会失败,因为标准缺少用于 boost 使用的文件流的 wide_char open() API。见Read/Write file with unicode file name with plain C++/BoostUTF-8-compliant IOstreamshttps://svn.boost.org/trac10/ticket/9968

但是我遇到了问题,这个问题主要发生在尝试使用系统代码页之外的字符时。在我的情况下,我只使用系统代码页中的字符,因为显然用户目录存在。这让我想,如果我可以告诉 boost::path 期望所有 std::strings 都作为 UTF8 编码但在调用 string() 成员函数时将它们转换为系统编码(这发生在 boost::fstream::open )

所以基本上:有没有办法使用 boost(和 boost Locale)自动进行转换(UTF8->系统编码)?

这里要完整的是我设置语言环境的代码:

#ifdef _WIN32
        // On windows we want to enforce the encoding (mostly UTF8). Also using "" would use the default which uses "wrong" separators
        std::locale::global(boost::locale::generator().generate("C"));
#else
        // In linux / OSX this suffices
        std::locale::global(std::locale::classic());
#endif // _WIN32
        // Use also the encoding (mostly UTF8) for bfs paths
        bfs::path::imbue(std::locale());

【问题讨论】:

    标签: c++ boost encoding utf-8


    【解决方案1】:

    在 Windows 上是个问题,因为 Windows 使用 UTF-16,而不是 UTF-8。我经常使用这个功能来解决你的问题:

    // get_filename_token.cpp
    
    // Turns a UTF-8 filename into something you can pass to fstream::open() on 
    // Windows. Returns the argument on other systems.
    
    // Copyright 2013 Michael Thomas Greer
    // Distributed under the Boost Software License, Version 1.0.
    // (See accompanying file LICENSE_1_0.txt 
    //  or copy at            http://www.boost.org/LICENSE_1_0.txt )
    
    #ifdef _WIN32
    
    #include <string>
    
    #ifndef NOMINMAX
    #define NOMINMAX
    #endif
    #include <windows.h>
    
    std::string get_filename_token( const std::string& filename )
      {
      // Convert the UTF-8 argument path to a Windows-friendly UTF-16 path
      wchar_t* widepath = new wchar_t[ filename.length() + 1 ];
      MultiByteToWideChar( CP_UTF8, 0, filename.c_str(), -1, widepath, filename.length() + 1 );
    
      // Now get the 8.5 version of the name
      DWORD n = GetShortPathNameW( widepath, NULL, 0 );
      wchar_t* shortpath = new wchar_t[ n ];
      GetShortPathNameW( widepath, shortpath, n );
    
      // Convert the short version back to a C++-friendly char version
      n = WideCharToMultiByte( CP_UTF8, 0, shortpath, -1, NULL, 0, NULL, NULL );
      char* ansipath = new char[ n ];
      WideCharToMultiByte( CP_UTF8, 0, shortpath, -1, ansipath, n, NULL, NULL );
    
      std::string result( ansipath );
    
      delete [] ansipath;
      delete [] shortpath;
      delete [] widepath;
    
      return result;
      }
    
    #else
    
    std::string get_filename_token( const std::string& filename )
      {
      // For all other systems, just return the argument UTF-8 string.
      return filename;
      }
    
    #endif
    

    (我已经删掉了一些东西在这里发布。)

    【讨论】:

    • 是的,我猜我需要走那条路。我什至在考虑创建一个新的 iofstream 类,它像 boost 类一样工作,只是提供新的开放函数和 ctors。你为什么要转换回 UTF8 呢? CP_ACP 不是更适合吗?为什么 boost 不这样,因为这似乎很简单。像 8.3 名称这样的缺点并不总是符合 ANSI 标准吗?
    • 发现缺点:这仅适用于现有文件。因此,需要确保文件确实存在,这可能会在尝试使用 Widechar 实现创建然后使用短路径方法打开时打开竞争条件的大门。
    • 因为它是跨平台的。所有现代 *nixen 都会打开一个 UTF-8 文件名,并且不会破坏旧代码。同样,所有 Windows 文件名可以转换为操作系统可接受的 8.3 文件名“令牌”,这在技术上是 UTF-8 子集。
    • 是的,如果你想创建一个包含非 ANSI 字符的文件,在 Windows 上你必须使用 UTF-16。这是另一个功能。
    • 似乎可以在操作系统级别禁用 8.3 路径,这也使得这不合适。但是我发现 Pathie 和 Boost.NoWide 可以工作 (cppcms.com/files/nowide/html, quintus.github.io/pathie-cpp/index.html)
    【解决方案2】:

    我找到了 2 个使用另一个库的解决方案,它们都有各自的缺点。

    1. Pathie (Docu) 它看起来完全替代了 boost::filesystem,提供 UTF8 感知流和路径处理以及符号链接创建和其他文件/文件夹操作。对获取特殊目录(temp、HOME、programs 文件夹等)的内置支持真的很酷
      缺点:仅作为动态库工作,因为静态构建有错误。如果您已经使用了 boost,也可能是矫枉过正。
    2. Boost.NoWide (Docu) 为几乎所有文件和流处理程序提供替代方案,以支持 Windows 上的 UTF8 并回退到其他标准功能。文件流接受 UTF8 编码值(用于名称)并且它使用 boost 本身。
      缺点:没有路径处理并且不接受bfs::path 或宽字符串(Windows 上的bfs::path 内部格式是UTF16),因此需要一个补丁,尽管它很简单。如果您想将std::cout 等与 UTF8 字符串一起使用,还需要为 Windows 构建(是的,可以直接使用!)
      另一个很酷的东西:它提供了一个类来在 windows 上将 argc/argv 转换为 UTF8。

    【讨论】:

      猜你喜欢
      • 2013-07-14
      • 2015-01-15
      • 1970-01-01
      • 1970-01-01
      • 2010-11-24
      • 1970-01-01
      • 2017-09-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多