C++ 在c+中打开utf8编码的文件名+;窗户

C++ 在c+中打开utf8编码的文件名+;窗户,c++,windows,C++,Windows,考虑以下代码: #include <iostream> #include <boost\locale.hpp> #include <Windows.h> #include <fstream> std::string ToUtf8(std::wstring str) { std::string ret; int len = WideCharToMultiByte(CP_UTF8, 0, str.c_str(), str.length

考虑以下代码:

#include <iostream>
#include <boost\locale.hpp>
#include <Windows.h>
#include <fstream>

std::string ToUtf8(std::wstring str)
{
    std::string ret;
    int len = WideCharToMultiByte(CP_UTF8, 0, str.c_str(), str.length(), NULL, 0, NULL, NULL);
    if (len > 0)
    {
        ret.resize(len);
        WideCharToMultiByte(CP_UTF8, 0, str.c_str(), str.length(), &ret[0], len, NULL, NULL);
    }
    return ret;
}

int main()
{
    std::wstring wfilename = L"D://Private//Test//एउटा फोल्दर//भित्रको फाईल.txt";
    std::string utf8path = ToUtf8(wfilename );
    std::ifstream iFileStream(utf8path , std::ifstream::in | std::ifstream::binary);
    if(iFileStream.is_open())
    {
        std::cout << "Opened the File\n";
        //Do the work here.
    }
    else
    {
        std::cout << "Cannot Opened the file\n";

    }
    return 0;

}
#包括
#包括
#包括
#包括
std::string ToUtf8(std::wstring str)
{
std::字符串ret;
int len=WideCharToMultiByte(CP_UTF8,0,str.c_str(),str.length(),NULL,0,NULL,NULL);
如果(len>0)
{
重新调整大小(len);
宽图表多字节(CP_UTF8,0,str.c_str(),str.length(),&ret[0],len,NULL,NULL);
}
返回ret;
}
int main()
{
std::wstring wfilename=L“D://Private//Test//एउटा फोल्दर//भित्रको फाईल.txt”;
std::string utf8path=ToUtf8(wfilename);
std::ifstream-iFileStream(utf8path,std::ifstream::in | std::ifstream::binary);
如果(iFileStream.is_open())
{
std::cout在Windows上,必须使用8位ANSI(并且必须与用户的区域设置相匹配)对于文件名或UTF-16,没有其他可用选项。您可以在主代码中继续使用
string
和UTF-8,但在打开文件时必须将UTF-8文件名转换为UTF-16。效率较低,但这是您需要做的

幸运的是,VC++对
std::ifstream
std::ofstream
的实现有非标准的构造函数重载和
open()
方法来接受UTF-16文件名的字符串

explicit basic_ifstream(
    const wchar_t *_Filename,
    ios_base::openmode _Mode = ios_base::in,
    int _Prot = (int)ios_base::_Openprot
);

void open(
    const wchar_t *_Filename,
    ios_base::openmode _Mode = ios_base::in,
    int _Prot = (int)ios_base::_Openprot
);
void open(
    const wchar_t *_Filename,
    ios_base::openmode _Mode
);
您必须使用<代码>·yIFDEF <代码>来检测Windows编译(不幸的是,不同的C++编译器标识不同),并且在打开文件时临时将UTF-8字符串转换为UTF-16。
#ifdef _MSC_VER
std::wstring ToUtf16(std::string str)
{
    std::wstring ret;
    int len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), NULL, 0);
    if (len > 0)
    {
        ret.resize(len);
        MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), &ret[0], len);
    }
    return ret;
}
#endif

int main()
{
    std::string utf8path = ...;
    std::ifstream iFileStream(
        #ifdef _MSC_VER
        ToUtf16(utf8path).c_str()
        #else
        utf8path.c_str()
        #endif
        , std::ifstream::in | std::ifstream::binary);
    ...
    return 0;
}

注意,这仅保证在VC++中工作。其他Windows的C++编译器不能保证提供类似的扩展。


更新:从Windows 10 Insider Preview Build 17035开始,Microsoft现在支持UTF-8作为系统范围的编码,用户可以将其区域设置为。从Windows 10版本1903(Build 18362)开始,应用程序现在可以通过其应用程序清单选择使用UTF-8作为进程范围的代码页,即使用户区域设置未设置为UTF-8。这些功能允许基于ANSI的API(如
CreateFileA()
,它
std::ifstream
/
std::ofstream
在内部使用)使用UTF-8字符串。因此,理论上,启用此功能后,您可以将UTF-8编码的字符串传递给
std::ifstream
/
std::ofstream
,它将“正常工作”。我不能确认这一点,因为这在很大程度上取决于实现。坚持传递UTF-16文件名会更安全,因为这是Windows的本机编码,ANSI API将在内部转换为。

没有任何底层Windows API使用UTF8。std::ifstream最终将调用CreateFileA或CreateFileW打开这些函数的下面的文件采用UTF8。因此,如果我要使用
ifstream
我应该如何更改代码以使其工作。我应该使用
wstring
吗?问题是我正在尝试使代码跨平台。既然Linux已经支持unicode,那么如果我使用
ifstream
,代码应该如何工作我能解决这个问题吗?这取决于您的标准库实现。我所熟悉的一个实现,实际上是不可能的,您不能将iostreams用于可能具有非8位文件名的文件。因此,我唯一的选择是使用
ifdefs
,对于windows使用
wstring
,对于Linux操作系统使用
string
?任何其他方法存在?+1这有效。对于那些想将
utf8
转换为
utf16
的人,还有另一个功能可用。有许多UTF转换实现可用。手动实现(如您链接的那个),Unicode库,如libiconv和ICU,甚至C++11中的
std::codevt_utf8_utf16
。您可以创建一个函数
filename(const std::string&fname),而不是将
#ifdef
放在每个打开的文件中
并将所有讨厌的东西放在一个地方。然后,只要在需要打开文件的地方对文件名使用该函数即可。@Raedwald不,我的意思是8bit ANSI。未在UTF中编码的Unicode字符串需要8位编码,如Windows-1252等(7bit ASCII是UTF-8的子集)。在Windows上,用户区域设置是使用实现这些编码的语言来实现的。因此,Windows系统上的文件名必须以UTF-16或用户默认的ANSI代码页进行编码。@jpo38您可以使用任何您想要实现的
ToUtf16()
。有很多Unicode API可供选择。
wstring\u convert()
将起作用,但请注意,它在C++17中已被弃用,尚未定义标准替换。
#ifdef _MSC_VER
std::wstring ToUtf16(std::string str)
{
    std::wstring ret;
    int len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), NULL, 0);
    if (len > 0)
    {
        ret.resize(len);
        MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), &ret[0], len);
    }
    return ret;
}
#endif

int main()
{
    std::string utf8path = ...;
    std::ifstream iFileStream(
        #ifdef _MSC_VER
        ToUtf16(utf8path).c_str()
        #else
        utf8path.c_str()
        #endif
        , std::ifstream::in | std::ifstream::binary);
    ...
    return 0;
}