从表示 Windows 上的任意文件名的 QString 获取 std::string 或 C 字符串
Getting a std::string or C string from a QString representing an arbitrary filename on Windows
我正在使用 QFileDialog::getOpenFileName()
让用户 select 一个文件,但我需要结果是一个 C 字符串,因为我必须将它传递给用 C 编写的东西,它使用fopen()
。我不能改变这个。
我发现的问题是,在 Windows/MinGW 上,对结果 QString
使用 toStdString()
不能很好地处理 Unicode/non-ASCII 文件名。尝试打开基于 std::string
的文件失败,因为某些字符集转换似乎正在发生。有时使用 toLocal8Bit()
转换有效,但有时无效。
考虑以下 (MinGW) 程序:
#include <cstdio>
#include <iostream>
#include <QApplication>
#include <QFileDialog>
#include <QFile>
int main(int argc, char **argv)
{
QApplication app(argc, argv);
auto filename = QFileDialog::getOpenFileName();
QFile f(filename);
std::cout << "fopen: " << (std::fopen(filename.toStdString().c_str(), "r") != nullptr) << std::endl;
std::cout << "fopen (local8bit): " << (std::fopen(filename.toLocal8Bit().data(), "r") != nullptr) << std::endl;
std::cout << "Qt can open: " << f.open(QIODevice::ReadOnly) << std::endl;
}
- 对于名为
☢.txt
的文件,toStdString()
有效,local8Bit()
无效。
- 对于名为
ä.txt
的文件,toStdString()
不起作用,local8Bit()
起作用。
- 对于名为
Ȁ.txt
的文件,两者均无效。
不过,在所有情况下,QFile
都可以打开文件。我想它可能使用 Unicode Windows 函数,而 C 代码使用 fopen()
,据我了解,这是 Windows 上的所谓 ANSI 函数。但是有没有什么办法可以从 QString
中得到一个“字节袋”呢?我不关心文件名的编码,我只想要一些可以传递给 fopen()
来打开文件的东西。
我发现使用 GetShortPathName
从 filename.toWCharArray()
获取短文件名似乎可行,但这非常麻烦,我的理解是 NTFS 文件系统可以被告知不支持短文件名名称,所以它通常不是一个可行的解决方案。
Windows non-unicode API 中的文件路径要么在当前 ANSI(Microsoft 编解码器)代码页中解析,要么在 OEM 代码页中解析(另请参阅 https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/fopen-wfope). ANSI 是默认值。
所以您的问题转化为:如何将 UTF-8 或 UTF-16 字符串转换为 ANSI 或 OEM?
有一个 ANSI 转换的答案:How to convert from UTF-8 to ANSI using standard c++
无论如何,重要的是要认识到并非所有 UTF 字符串都可以用这些更窄的编解码器来表示...
我正在使用 QFileDialog::getOpenFileName()
让用户 select 一个文件,但我需要结果是一个 C 字符串,因为我必须将它传递给用 C 编写的东西,它使用fopen()
。我不能改变这个。
我发现的问题是,在 Windows/MinGW 上,对结果 QString
使用 toStdString()
不能很好地处理 Unicode/non-ASCII 文件名。尝试打开基于 std::string
的文件失败,因为某些字符集转换似乎正在发生。有时使用 toLocal8Bit()
转换有效,但有时无效。
考虑以下 (MinGW) 程序:
#include <cstdio>
#include <iostream>
#include <QApplication>
#include <QFileDialog>
#include <QFile>
int main(int argc, char **argv)
{
QApplication app(argc, argv);
auto filename = QFileDialog::getOpenFileName();
QFile f(filename);
std::cout << "fopen: " << (std::fopen(filename.toStdString().c_str(), "r") != nullptr) << std::endl;
std::cout << "fopen (local8bit): " << (std::fopen(filename.toLocal8Bit().data(), "r") != nullptr) << std::endl;
std::cout << "Qt can open: " << f.open(QIODevice::ReadOnly) << std::endl;
}
- 对于名为
☢.txt
的文件,toStdString()
有效,local8Bit()
无效。 - 对于名为
ä.txt
的文件,toStdString()
不起作用,local8Bit()
起作用。 - 对于名为
Ȁ.txt
的文件,两者均无效。
不过,在所有情况下,QFile
都可以打开文件。我想它可能使用 Unicode Windows 函数,而 C 代码使用 fopen()
,据我了解,这是 Windows 上的所谓 ANSI 函数。但是有没有什么办法可以从 QString
中得到一个“字节袋”呢?我不关心文件名的编码,我只想要一些可以传递给 fopen()
来打开文件的东西。
我发现使用 GetShortPathName
从 filename.toWCharArray()
获取短文件名似乎可行,但这非常麻烦,我的理解是 NTFS 文件系统可以被告知不支持短文件名名称,所以它通常不是一个可行的解决方案。
Windows non-unicode API 中的文件路径要么在当前 ANSI(Microsoft 编解码器)代码页中解析,要么在 OEM 代码页中解析(另请参阅 https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/fopen-wfope). ANSI 是默认值。
所以您的问题转化为:如何将 UTF-8 或 UTF-16 字符串转换为 ANSI 或 OEM?
有一个 ANSI 转换的答案:How to convert from UTF-8 to ANSI using standard c++
无论如何,重要的是要认识到并非所有 UTF 字符串都可以用这些更窄的编解码器来表示...