C++ MinGW下带有boost::filestream的UTF-8名称

C++ MinGW下带有boost::filestream的UTF-8名称,c++,boost,encoding,utf-8,C++,Boost,Encoding,Utf 8,boost filestreams遇到了一个问题:我需要在windows下的用户目录中创建和修改文件。然而,用户名包含一个umlaut,当在MinGW下编译时,由于该标准缺少boost使用的文件流的wide_char open()API,这使得此操作失败。看到了吗 然而,我无意中发现,这个问题主要发生在试图使用系统代码页之外的字符时。在我的例子中,我只使用系统代码页中的字符,因为显然用户目录存在。这让我想到,如果我可以告诉boost::path,希望所有std::strings都是UTF8,但在

boost filestreams遇到了一个问题:我需要在windows下的用户目录中创建和修改文件。然而,用户名包含一个umlaut,当在MinGW下编译时,由于该标准缺少boost使用的文件流的wide_char open()API,这使得此操作失败。看到了吗

然而,我无意中发现,这个问题主要发生在试图使用系统代码页之外的字符时。在我的例子中,我只使用系统代码页中的字符,因为显然用户目录存在。这让我想到,如果我可以告诉boost::path,希望所有
std::string
s都是UTF8,但在调用
string()
成员函数时将它们转换为系统编码(发生在
boost::fstream::open
中),那么这应该是可行的

那么基本上:有没有任何方法可以使用boost(和boost语言环境)自动进行转换(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());

这在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
//获取\u filename\u token.cpp
//将UTF-8文件名转换为可以传递给上的fstream::open()的文件名
//窗户。返回其他系统上的参数。
//版权所有2013迈克尔·托马斯·格里尔
//根据Boost软件许可证1.0版发布。
//(请参阅随附的文件LICENSE_1_0.txt)
//或抄送http://www.boost.org/LICENSE_1_0.txt )
#ifdef_WIN32
#包括
#ifndef NOMINMAX
#定义NOMINMAX
#恩迪夫
#包括
std::string get\u filename\u令牌(const std::string和filename)
{
//将UTF-8参数路径转换为Windows友好的UTF-16路径
wchar_t*widepath=new wchar_t[filename.length()+1];
MultiByteToWideChar(CP_UTF8,0,filename.c_str(),-1,widepath,filename.length()+1);
//现在获取名称的8.5版本
DWORD n=GetShortPathNameW(宽路径,NULL,0);
wchar_t*shortpath=新的wchar_t[n];
GetShortPathNameW(宽路径,短路径,n);
//将短版本转换回C++友好的字符版本
n=宽图表多字节(CP_UTF8,0,短路径,-1,空,0,空,空);
char*ansipath=新字符[n];
宽图表多字节(CP_UTF8,0,短路径,-1,ansipath,n,NULL,NULL);
std::字符串结果(ansipath);
删除[]ansipath;
删除[]短路径;
删除[]宽路径;
返回结果;
}
#否则
std::string get\u filename\u令牌(const std::string和filename)
{
//对于所有其他系统,只需返回参数UTF-8字符串。
返回文件名;
}
#恩迪夫

(我在这里列出了一些内容。)

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

  • ()它看起来像是boost::filesystem的完全替代品,提供UTF8感知流和路径处理以及符号链接创建和其他文件/文件夹操作。真正酷的是内置支持获取特殊目录(临时目录、主目录、程序文件夹等)
    缺点:只作为动态库使用,因为静态构建有bug。如果您已经使用了boost,那么这也可能是过火了
  • ()提供了几乎所有文件和流处理程序的替代方案,以在windows上支持UTF8,并在其他系统上使用标准函数。filestreams接受UTF8编码的值(名称),并使用boost本身。
    缺点:没有路径处理,不接受
    bfs::path
    或宽字符串(
    bfs::path
    Windows上的内部格式为UTF16),因此需要一个补丁,尽管它很简单。如果要将
    std::cout
    etc与UTF8字符串一起使用(是的,可以直接使用!),还需要windows版本
    另一件很酷的事情:它提供了一个类来在windows上将argc/argv转换为UTF8

  • 是的,我想我需要走那条路。我甚至在考虑创建一个新的iofstream类,它的工作原理与boost类类似,只是提供了新的开放函数和构造函数。为什么要转换回UTF8呢?
    CP\u ACP
    不是更合适吗?为什么boost不这么做,因为这看起来很简单。有没有像8.3名称不总是符合ANSI之类的缺点?找到了退步:这只适用于现有文件。因此,我们需要确保该文件确实存在,这可能会在尝试实现使用widechar创建然后使用短路径方法打开时打开竞争条件的大门,因为它是跨平台的。所有modern*nixen都会打开一个UTF-8文件名,并且不会破坏旧代码。同样,所有Windows文件名都可以转换为操作系统可接受的8.3文件名“令牌”,从技术上讲,它是UTF-8子集。是的,如果要创建包含非ANSI字符的文件,则必须在Windows上使用UTF-16。这是另一个功能。似乎8.3路径可以在操作系统级别上禁用,这也使得它不适合。但是我发现Pathie和Boost.NoWide可以工作(,)