Qt UTF-8 文件到 std::string 添加额外的字符

Qt UTF-8 File to std::string Adds extra characters

本文关键字:字符 添加 string 文件 std Qt UTF-8      更新时间:2023-10-16

我有一个UTF-8编码的文本文件,其中包含²,³,Ç和ó等字符。当我使用以下命令读取文件时,该文件似乎被正确读取(至少根据我在查看contents变量的内容时在Visual Studio编辑器中看到的内容)

QFile file( filePath );
if ( !file.open( QFile::ReadOnly | QFile::Text ) ) {
return;
}
QString contents;
QTextStream stream( &file );
contents.append( stream.readAll() );
file.close();

但是,一旦内容转换为std::string,就会添加其他字符。例如,²被转换为²,而它应该只是 ²。这似乎发生在每个非 ANSI 字符上,添加了额外的Â,这当然意味着在保存新文件时,输出文件中的字符不正确。

当然,我尝试过简单地做toStdString(),我也尝试过toUtf8,甚至尝试使用QTextCodec但每个都未能给出正确的值。

我不明白为什么从 UTF-8 文件到 QString,然后到 std::string 会丢失 UTF-8 字符。它应该能够重现最初读取的确切文件,还是我完全丢失了某些内容?

正如Daniel Kamil Kozar在他的回答中提到的,QTextStream不会读取编码,因此实际上并没有正确读取文件。QTextStream必须在读取文件之前设置其编解码器,以便正确分析字符。在下面的代码中添加了注释以显示所需的额外文件。

QFile file( filePath );
if ( !file.open( QFile::ReadOnly | QFile::Text ) ) {
return;
}
QString contents;
QTextStream stream( &file );
stream.setCodec( QTextCodec::codecForName( "UTF-8" ) ); // This is required.
contents.append( stream.readAll() );
file.close();

您看到的实际上是预期的行为。

字符串²由编码为 UTF-8 时C3 82 C2 B2的字节组成。假设QTextStream实际上正确识别了 UTF-8(从文档来看,这并不是那么明显,该文档仅在存在 BOM 时提到字符编码检测,并且您没有说任何关于输入文件具有 BOM 的内容),我们可以假设QTextStream::readAll返回的QString实际上包含字符串²

QString::toStdString()返回给定QString表示的字符串的 UTF-8 编码变体,因此返回值应包含与输入文件相同的字节 - 即C3 82 C2 B2

现在,关于您在调试器中看到的内容:

  1. 您在其中一条评论中指出"QString 在字符串中只有0xC2 0xB2(这是正确的)。QString在内部使用UTF-16LE,这意味着它的内部字符数组包含两个16位值:0x00C2 0x00B2。事实上,当每个字符被编码为 UTF-16 时,这些映射到字符²,这证明QString是根据文件的输入正确构造的。但是,调试器似乎足够聪明,知道构成QString的字节以 UTF-16 编码,从而正确呈现字符。
  2. 您还表示调试器将从QString::toStdString返回的std::string的内容显示为²。假设您的调试器在没有明确说明编码的情况下使用可怕的"ANSI 代码页"将字节解析为字符,并且您使用的是使用 Windows-1252 作为其默认旧代码页的英语 Windows,那么一切都适合:std::string实际上包含字节C3 82 C2 B2,映射到 Windows-1252 中²字符。

无耻的自我插拔:我去年在一次会议上发表了关于字符编码的演讲。也许观看它可以帮助您更好地理解其中的一些问题。

最后一件事:ANSI不是一种编码。它可能意味着基于Windows区域设置的许多不同的编码。