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

Phy*_*137 9 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
}
Run Code Online (Sandbox Code Playgroud)

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

Gui*_*nal 8

您需要了解编码的一些基本概念.我建议阅读这篇文章:绝对最低每个软件开发人员绝对必须知道Unicode和字符集.基本上你不能假设每个字节都是一个字母,每个字母都适合一个字母char.此外,系统必须知道如何从文件中的字节序列中提取字母.

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

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

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

/  /     o   l       á
47 47 32 111 108 195 161
Run Code Online (Sandbox Code Playgroud)

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

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

我们去吧wstring.他们存储每个字母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;
}
Run Code Online (Sandbox Code Playgroud)

你可能会问什么std::ios::sync_with_stdio(false);意思.这是必需的,因为默认情况下C++流与C流保持同步.这是一件好事,因为,您可以同时使用cout,并printf在相同的程序.我们必须禁用它,因为C流将破坏utf-8编码并将在输出上产生垃圾.