在我正在研究的项目中,我处理了很多字符串操作; 从二进制文件中读取字符串及其编码(可以是单字节或双字节).本质上,我读取字符串值为vector<char>,读取编码,然后将所有字符串转换为wstring,以保持一致性.
这工作得相当好,但文件名本身可以是双字节字符.我完全不知道如何实际打开输入流.在CI中会使用_wfopen函数传递wchar_t* path,但wifstream似乎表现不同,因为它专门用于从文件中读取双字节字符,而不是从具有双字节文件名的文件中读取单个字节.
这个问题的解决方案是什么?
编辑:搜索网络,看起来在标准C++中根本不支持这一点(例如,请参阅此讨论).但是我想知道C++ 11是否真的在这方面添加了一些有用的东西.
这是对STL的抱怨.为什么他们将文件名参数作为(char*)而不是作为std :: string?这似乎毫无意义.
关于这个主题还有另外两个问题:
问题是我有很多代码如下:
std::ofstream f(fname.c_str());
Run Code Online (Sandbox Code Playgroud)
当我希望它看起来像这样:
std::ofstream f(fname);
Run Code Online (Sandbox Code Playgroud)
上述帖子中提到的其他问题是UTF-16与UTF-8的问题.(UTF-16可能包含会破坏POSIX API的NULL).但这不是一个真正的问题,因为实现可以在调用open()之前将UTF-16转换为UTF-8.
但严重的是,这没有任何意义.有没有计划升级STL?
从这个答案中,我了解到在C ++ 17中,我们可以std::fstream通过使用UTF-8路径打开std::filesystem::u8path。但是在C ++ 20中,不建议使用此函数,而应该将其传递const char8_t*给std::filesystem::path构造函数。
问题来了:尽管我们可以合法地(通过reinterpret_cast)将任何指针转换为const char*,但我们不能向后做:from const char*到eg const char8_t*(这会破坏严格的别名规则)。因此,如果我们有一些外部API返回char文件名的基于UTF-8的表示形式(例如,使用C语言编写的库),则无法安全地将指针转换为char8_t基于文件的指针。
那么,我们应该如何将这种char基于UTF-8字符串char8_t的视图转换为基于它们的UTF-8字符串?
我正在修复一个大型开源跨平台应用程序,以便它可以处理 Windows 上包含非 ANSI 字符的文件路径。
更新:
根据到目前为止我得到的答案和评论(谢谢!)我觉得我应该澄清一些观点:
我无法修改几十个第三方库的代码来使用std::wchar_t。这不是一个选择。该解决方案必须与普通的 ol'等一起std::fopen()使用。std::ifstream
我在下面概述的解决方案的运行率为 99%,至少在我正在开发的系统上(Windows 10 版本 1909,内部版本 18363.535)。我还没有在任何其他系统上进行过测试。
唯一剩下的问题,至少在我的系统上,基本上是数字格式,我希望替换方面std::numpunct能够解决问题(但我还没有成功)。
我当前的解决方案包括:
将 Windows 上的类别的C 区域设置设置为.UTF-8(LC_CTYPE所有其他类别均设置为C应用程序所需的区域设置):
// Required by the application.
std::setlocale(LC_ALL, "C");
// On Windows, we want std::fopen() and other functions dealing with strings
// and file paths to accept narrow-character strings encoded in UTF-8.
#ifdef _WIN32
{
#ifndef NDEBUG
char* new_ctype_locale =
#endif
std::setlocale(LC_CTYPE, …Run Code Online (Sandbox Code Playgroud)客户抱怨我们的代码用于在文件名中写入带有日文字符的文件,但在所有情况下都不再有效.我们总是使用好的旧char*字符串来表示文件名,所以它对我来说有点震撼它曾经有效,我们没有做任何我知道应该让它停止工作的事情.我让他们发给我一个带有嵌入式文件名的文件,从我们的软件导出它,看起来字符串使用十六进制字符82和83作为双字节序列的第一个字符来表示日文字符.在线浏览让我相信这可能是SHIFT_JIS和/或Windows代码页932.
在我看来,之前发生的事情是fopen和ofstream ::使用此代码页打开接受的文件名; 现在只有fopen呢.我已经检查了Visual Studio fopen docs,我没有看到什么使得可接受的字符串传递给fopen.
从短期来看,我希望有人可以为我提供一些特定的Windows fopen与ofstream :: open问题.从长远来看,我真的想知道在Windows,Linux和OS X上用C++打开Unicode(和其他?)文件名的可接受方式.
编辑添加:我相信打开工作是在"C"语言环境中完成的,而那些不工作的打开是在客户的默认语言环境中完成的.然而,多年以来一直如此,该程序的旧版本今天仍然适用于他们的系统,所以这似乎是解释我们所看到的问题的一个远景.
更新:我向客户发送了一个小测试程序.它已经验证fopen与SHIFT_JIS文件名一起工作正常,而std :: ofstream则没有.这是在Visual Studio 2005中,无论我使用的是默认语言环境还是"C"语言环境,都会发生这种情况.
我仍然感兴趣,如果有人对此行为有解释(以及为什么它神秘地改变了 - 也许是VS2005的服务包?)并希望整合一个全面的"最佳实践"来处理便携式C++代码中的Unicode文件名.
我想从磁盘读取文件,在程序执行期间使用QDialog(Qt Widget)我选择文件的路径.一段代码很简单:
ifstream infile(path.c_str());
if (infile.is_open()) {
//some code
}
else
//log it
Run Code Online (Sandbox Code Playgroud)
问题出现取决于目标文件的位置:
怎么解决这个问题?