在MinGW下使用boost :: filestream的UTF-8名称

时间:2017-09-25 03:00:56

标签: c++ boost encoding utf-8

我遇到了升级文件流的问题:我需要在windows下的用户目录中创建和修改文件。但是,用户名包含一个变音符号,这使得在MinGW下编译时失败,因为标准缺少用于提升使用的文件流的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::string为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());

2 个答案:

答案 0 :(得分: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

(我已经删除了一些要发布的内容。)

答案 1 :(得分:1)

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

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