如何在Linux上用std :: ifstream读取文件中的非ASCII行?

时间:2014-08-13 14:18:35

标签: c++ linux

我试图读取纯文本文件。在我的情况下,我需要读取每行的行,并处理该信息。我知道C ++有阅读wchars的功能。我尝试了以下方法:

#include <fstream>
#include <iostream>

int main() {
    std::wfstream file("file");       // aaaàaaa
    std::wstring str;
    std::getline(file, str);
    std::wcout << str << std::endl;   // aaa
}

但正如你所看到的,它并没有读完整行。读取“à”时会停止,这是非ASCII。我该如何解决?

1 个答案:

答案 0 :(得分:8)

您需要了解编码的一些基本概念。我建议阅读这篇文章:The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets。基本上你不能假设每个字节都是一个字母,并且每个字母都适合char。此外,系统必须知道如何从文件中的字节序列中提取字母。

假设您的文件是以UTF-8编码的,这可能是因为您使用的是Linux。我假设您的终端也支持它。如果您使用std::string直接阅读,使用字符,您将拥有一切正常运作。看:

// olá
#include <iostream>
#include <fstream>
int main() {
    std::fstream file("test.cpp");
    std::string str;
    std::getline(file, str);
    std::cout << str << std::endl;
}

输出是你所期望的,但这不是真的正确。看看发生了什么:文件以utf-8编码。这意味着第一行是这个字节序列:

/  /     o   l       á
47 47 32 111 108 195 161

请注意á is encoded with two bytes。如果你询问字符串的大小(str.size()),你的确会得到错误的值:7。发生这种情况是因为字符串认为每个字节都是char。当您将其发送到std::cout时,该字符串将被提供给终端进行打印。神奇的部分:终端默认使用utf-8。所以它只是假设字符串是utf-8并正确打印6个字符。

你知道它有效,但它确实不对。尝试对数据进行任何字符串操作,您可能会破坏utf-8编码,永远无法再次打印!

让我们去wstring。它们用wchar_t存储每个字母,在Linux上有4个字节。这足以容纳任何可能的unicode字符。但它不会直接起作用,因为默认情况下C ++使用"C"语言环境。区域设置是如何处理系统的各个方面的规范,例如&#34;如何打印日期&#34;或&#34;如何格式化货币价值&#34;甚至&#34;如何解码文本&#34;。最后一个因素很重要,默认的"C"编码说:&#34;假设一切都是ASCII&#34;。当它正在读取文件并尝试解码非ASCII字节时,它就会无声地失败。

更正很简单:使用UTF-8语言环境。看:

// olá
#include <iostream>
#include <fstream>
#include <locale>

int main() {
    std::ios::sync_with_stdio(false);

    std::locale loc("en_US.UTF-8"); // You can also use "" for the default system locale
    std::wcout.imbue(loc); // Use it for output

    std::wfstream file("test.cpp");
    file.imbue(loc); // Use it for file input
    std::wstring str;
    std::getline(file, str); // str.size() will be 6
    std::wcout << str << std::endl;
}

您可能会问std::ios::sync_with_stdio(false);的含义。这是必需的,因为默认情况下C ++流与C流保持同步。这很好,因为您可以在同一个程序中同时使用coutprintf。我们必须禁用它,因为C流将破坏utf-8编码并在输出上产生垃圾。