在std::string和QString之间保留非ASCII字符

3
在我的程序中,用户可以通过命令行或使用“QFileDialog”提供文件名。在第一种情况下,我有一个没有任何编码信息的“char *”,在第二种情况下,我有一个“QString”。
为了将文件名存储以备后用(最近使用的文件),我需要它作为“QString”。但是,为了使用“std :: ifstream”打开文件,我需要一个“std :: string”。
现在开始有趣的事情了。我可以做以下操作:
filename = QString::fromLocal8Bit(argv[1]);

后来,我可以做:

std::string fn = filename.toLocal8Bit().constData();

这适用于大多数字符,但并非全部。例如,单词 Раи́са 经过此转换后看起来一样,但实际上包含不同的字符。 因此,我可以拥有一个名为 Раи́са.txt 的文件,它会显示 Раи́са.txt,但在文件系统中却无法找到它。大多数字母都可以正常工作,但 и́ 不行。 (请注意,当文件是在 QFileDialog 中选择时,它的效果是正确的。但它从命令行发起时则不行。) 有没有更好的方法来保留文件名?现在,我以任何本地编码方式获取文件名,并且可以在不知道其编码方式的情况下传递它。至少我是这么认为的。

为什么要使用ifstream打开文件,而不是QFile? - TheDarkKnight
1
该文件在另一个没有Qt依赖的模块中打开。它与GUI解耦。 - ypnos
1
为什么要使用 QString::fromLocal8Bit?从该函数的文档中可以看到:“QTextCodec::codecForLocale() 用于执行从 Unicode 的转换。如果无法确定区域编码,则此函数与 toLatin1() 相同。” 看起来在您的情况下最终调用了 toLatin1(),当您稍后调用 toLocal8Bit() 时会发生以下情况:“如果此字符串包含任何无法在区域设置中编码的字符,则返回的字节数组未定义。这些字符可能被抑制或替换为其他字符。” - Max Go
如果你使用的是Qt5,你试过这样吗?只需使用QString filename(argv[1]);,稍后再使用std::string fn = QByteArray(filename).constData(); - Max Go
@N1ghtLight 我也有同样的想法,但他在 QString filename 中看到了正确的字符串,所以他的 codecForLocale 必须设置正确,因为如果调用 toLatin1,特殊字符将被“抑制或替换为问号。” http://qt-project.org/doc/qt-5/qstring.html#toLatin1 - Jonathan Mee
显示剩余2条评论
1个回答

1
'

'и́' 不是 ASCII 字符,也就是说它没有 8 位表示。它在 argv[1] 中的表示方式取决于操作系统。但它不仅仅被表示为一个 char

fromLocal8bit 使用与 toLocal8bit 相同的 QTextCodec::codecForLocale。而且正如你所说的,你的 std::string 将保存 "Раи́са.txt",所以这不是问题。

'
根据您的操作系统如何定义std :: ifstreamstd :: ifstream可能期望每个char都是它自己的char,而不是经过操作系统的转换。我猜您正在使用Windows,因为您看到了这个问题。在这种情况下,您应该使用std :: fstreamstd :: wstring实现,这是Microsoft特定的:http://msdn.microsoft.com/en-us/library/4dx08bh4.aspx 您可以通过使用以下方法从QString获取std :: wstringtoStdWString 有关更多信息,请参见此处:fstream :: open()Unicode或非ASCII字符在Windows上无法正常工作(使用std :: ios :: out)
编辑:
一个适用于跨平台且有访问权限的项目的好选择是 Boost::Filesystemypnos提到File-Streams与此特别相关。

实际上它是跨平台的,我在Linux上进行了测试。这就是为什么我现在避免使用wstring的原因。 - ypnos
1
@ypnos 我不是 Linux 专家,但我知道它使用 UTF-8 作为文件系统。因此,在 Windows 上您可能需要使用 std::wstring 和在 Linux 上使用 QString::toUtf8,并使用 #ifdef 进行区分。更清晰的替代方案是使用 Boost:Filesystem,但我理解您可能会避免使用它。 - Jonathan Mee
这是一个好建议。在Linux上,现在大多数情况下都使用UTF8,但并非全部。还有OS X。Boost:Filesystem是个好主意。从你的建议中,我找到了http://www.boost.org/doc/libs/1_47_0/libs/filesystem/v3/doc/reference.html#File-streams。 - ypnos
1
@ypnos 是的,这件事的好处在于我相信所有基于path的东西都在tr2中,所以它应该会进入下一个重大的C++更新。因此,与其学习一些你永远不会再次使用的东西,你正在学习C++的未来! - Jonathan Mee
哈哈,这是一则不错的 Boost 广告。我的项目中已经有了 Boost:Filesystem 依赖,所以我正在认真考虑它。 - ypnos
@ypnos 哦,那样的话你绝对应该这么做,Boost非常跨平台友好。我会添加评论以供后人参考。 - Jonathan Mee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接