【问题标题】:How to read non-ASCII lines from file with std::ifstream on Linux?如何在 Linux 上使用 std::ifstream 从文件中读取非 ASCII 行?
【发布时间】:2014-10-06 22:58:08
【问题描述】:

我试图读取纯文本文件。就我而言,我需要逐行读取,并处理该信息。我知道 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 的“à”时停止。我该如何解决?

【问题讨论】:

  • "wchar" ≠ "non-ASCII",您可能会将任何旧的 8 位 编码误认为是“ASCII”。然而,这不是问题。这可能是 stackoverflow.com/questions/4775437/… 的副本
  • @Jongware,我正在寻找 Linux 解决方案。这似乎只适用于 Windows。
  • @Niall,不相关,也不重复。这些解决方案似乎使用了 codecvt,它在 gcc 上不起作用。
  • @Physicist137,注意到(我看到了新标签)。未标记为重复。但总的来说,wfstream 实现由一个缓冲区支持,该缓冲区仅读取char 类型,然后通过binary 访问或对区域设置进行一些转换来读取任何 unicode 要求。

标签: c++ linux


【解决方案1】:

您需要了解一些基本的编码概念。我推荐阅读这篇文章: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。发生这种情况是因为字符串认为每个字节都是一个字符。当您将其发送到std::cout 时,该字符串将被提供给终端进行打印。神奇的部分:终端默认使用 utf-8。所以它只是假设字符串是 utf-8 并正确打印 6 个字符。

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

让我们去找wstrings。他们用wchar_t 存储每个字母,在Linux 上,它有4 个字节。这足以容纳任何可能的 Unicode 字符。但它不会直接工作,因为 C++ 默认使用 "C" 语言环境。语言环境是如何处理系统各个方面的规范,例如“如何打印日期”或“如何格式化货币值”甚至“如何解码文本”。最后一个因素很重要,默认的"C" 编码表示:“假设一切都是ASCII”。当它正在读取文件并尝试解码非 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 编码并在输出中产生垃圾。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-13
    相关资源
    最近更新 更多