C++ MinGW下带有boost::filestream的UTF-8名称
boost filestreams遇到了一个问题:我需要在windows下的用户目录中创建和修改文件。然而,用户名包含一个umlaut,当在MinGW下编译时,由于该标准缺少boost使用的文件流的wide_char open()API,这使得此操作失败。看到了吗 然而,我无意中发现,这个问题主要发生在试图使用系统代码页之外的字符时。在我的例子中,我只使用系统代码页中的字符,因为显然用户目录存在。这让我想到,如果我可以告诉boost::path,希望所有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,但在
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字符串。
返回文件名;
}
#恩迪夫
(我在这里列出了一些内容。)我找到了两个使用另一个库的解决方案,它们都有各自的缺点
缺点:只作为动态库使用,因为静态构建有bug。如果您已经使用了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可以工作(,)