【问题标题】:What is the most elegant way to read a text file with c++?用 C++ 读取文本文件最优雅的方法是什么?
【发布时间】:2010-09-16 18:09:15
【问题描述】:

我想用 c++ 将文本文件的全部内容读入 std::string 对象。

使用 Python,我可以编写:

text = open("text.txt", "rt").read()

它非常简单优雅。我讨厌丑陋的东西,所以我想知道 - 用 C++ 读取文本文件的最优雅的方法是什么? 谢谢。

【问题讨论】:

  • 如果你讨厌丑陋的东西,你最好不要使用 C++ :P
  • 关于优雅的说明,即使最优雅的 iostream 解决方案在您看来仍然很丑陋,但您可以封装在一个漂亮的函数中,这样它就不会伤害您的眼睛 ;)
  • 关于“丑陋的东西”论点:while(ugly()) encapsulate_more();

标签: c++ text file-io


【解决方案1】:

有很多方法,你选择最适合你的。

读入 char*:

ifstream file ("file.txt", ios::in|ios::binary|ios::ate);
if (file.is_open())
{
    file.seekg(0, ios::end);
    size = file.tellg();
    char *contents = new char [size];
    file.seekg (0, ios::beg);
    file.read (contents, size);
    file.close();
    //... do something with it
    delete [] contents;
}

进入 std::string:

std::ifstream in("file.txt");
std::string contents((std::istreambuf_iterator<char>(in)), 
    std::istreambuf_iterator<char>());

转入向量:

std::ifstream in("file.txt");
std::vector<char> contents((std::istreambuf_iterator<char>(in)),
    std::istreambuf_iterator<char>());

转化成字符串,使用stringstream:

std::ifstream in("file.txt");
std::stringstream buffer;
buffer << in.rdbuf();
std::string contents(buffer.str());

file.txt 只是一个示例,二进制文件也可以正常工作,只要确保在 ifstream 构造函数中使用 ios::binary。

【讨论】:

  • 我比我更喜欢你的回答,这不是我经常说的话。好工作! +1
  • 您实际上需要使用 istreambuf_iterator 在内容的构造函数的第一个参数周围加上一组括号,以防止它被视为函数声明。
  • 删除 char* 版本中缺少的 []?
  • @Shadow2531:我认为它不应该被删除,直到你完成对它的处理。
  • @Ferruccio:请参阅 Greg Rogers 的上述评论。
【解决方案2】:

another thread 在这个主题上。

我在这个帖子中的解决方案(都是单行):

nice(参见米兰的第二个解决方案):

string str((istreambuf_iterator<char>(ifs)), istreambuf_iterator<char>());

和快速:

string str(static_cast<stringstream const&>(stringstream() << ifs.rdbuf()).str());

【讨论】:

  • 其实第一个更快,因为它直接对istream缓冲区进行操作,而后者依赖于第一个但增加了一些失败状态位。
  • @t.g.第一个使用非常低效的副本来构造字符串而无需事先分配,这会导致大量重新分配。第二个预分配所需大小的缓冲区。
  • 我刚刚用VC++10测试过。这实际上取决于。这取决于文件大小,第一个对于较小的文件更快,第二个对于较大的文件更快,这似乎证明了您所说的。
  • string str((istreambuf_iterator&lt;char&gt;(ifs))); 对我来说很好用,有什么问题?
  • @ThomasE 它使用了一个不存在的constructor overload。不知道为什么它可以在你的编译器上工作,或者它到底是做什么的。
【解决方案3】:

您似乎将优雅视为“小代码”的明确属性。这当然在某种程度上是主观的。有人会说省略所有错误处理不是很优雅。有人会说,您立即理解的清晰紧凑的代码很优雅。

编写您自己的读取文件内容的单行函数/方法,但在表面下使其严谨和安全,您将涵盖优雅的两个方面。

一切顺利

/罗伯特

【讨论】:

  • 推论:优雅就是优雅;优雅代码的概念在语言和范式之间有所不同。 C++ 程序员可能认为优雅的东西对于 Ruby 或 Python 程序员来说可能是可怕的,反之亦然。
【解决方案4】:

但请注意,c++ 字符串(或更具体的:STL 字符串)与能够保存任意长度字符串的 C 字符串一样小 - 当然不是!

查看成员 max_size(),它为您提供了字符串可能包含的最大字符数。这是一个实现定义的数字,可能无法在不同平台之间移植。 Visual Studio 为字符串提供大约 4gigs 的值,其他人可能只给你 64k,而在 64 位平台上它可能会给你一些非常大的东西!这取决于,当然通常你会在达到 4gig 限制之前很长时间因为内存耗尽而遇到 bad_alloc-exception...

顺便说一句:max_size() 也是其他 STL 容器的成员!它将为您提供该容器(理论上)能够容纳的某种类型(您为其实例化容器)的元素的最大数量。

因此,如果您正在读取来源不明的文件,您应该:
- 检查它的大小并确保它小于 max_size()
- 捕获并处理 bad_alloc-exceptions

还有一点: 你为什么热衷于将文件读入字符串?我希望通过增量解析或其他方式来进一步处理它,对吗?因此,与其将其读入字符串,不如将其读入字符串流(基本上只是字符串的一些语法糖)并进行处理。但是您也可以直接从文件中进行处理。因为如果正确编程,字符串流可以无缝地被文件流替换,即。 e.由文件本身。或者也可以通过任何其他输入流,它们都共享相同的成员和运算符,因此可以无缝互换!

对于处理本身:编译器还可以实现很多自动化!例如。假设您要标记字符串。定义适当的模板时,请执行以下操作:
- 从文件(或字符串或任何其他输入流)中读取
- 标记内容
- 将所有找到的令牌推入 STL 容器
- 按字母顺序对标记进行排序
- 消除任何双值
所有(!!)都可以在一行(!)C++代码中实现(撇开模板本身和错误处理不谈)!这只是函数 std::copy() 的一次调用!只需谷歌搜索“令牌迭代器”,您就会明白我的意思。所以在我看来,这比仅仅从文件中读取更“优雅”......

【讨论】:

  • 值得注意的是,max_size() 是相对于 size_t 的大小定义的,它是相对于您平台的位大小的。它以这种方式定义,以允许字符串与您的平台可以处理的一样大。
【解决方案5】:

我喜欢 Milan 的 char* 方式,但使用 std::string。


#include <iostream>
#include <string>
#include <fstream>
#include <cstdlib>
using namespace std;

string& getfile(const string& filename, string& buffer) {
    ifstream in(filename.c_str(), ios_base::binary | ios_base::ate);
    in.exceptions(ios_base::badbit | ios_base::failbit | ios_base::eofbit);
    buffer.resize(in.tellg());
    in.seekg(0, ios_base::beg);
    in.read(&buffer[0], buffer.size());
    return buffer;
}

int main(int argc, char* argv[]) {
    if (argc != 2) {
        cerr << "Usage: this_executable file_to_read\n";
        return EXIT_FAILURE;
    }
    string buffer;
    cout << getfile(argv[1], buffer).size() << "\n";
}

(带或不带 ios_base::binary,取决于您是否要转换换行符。您还可以将 getfile 更改为只返回一个字符串,这样您就不必传入缓冲区字符串。然后,测试看看编译器在返回时是否优化了复制出来。)

但是,这可能看起来会好一些(而且会慢很多):


#include <iostream>
#include <string>
#include <fstream>
#include <cstdlib>
using namespace std;

string getfile(const string& filename) {
    ifstream in(filename.c_str(), ios_base::binary);
    in.exceptions(ios_base::badbit | ios_base::failbit | ios_base::eofbit);
    return string(istreambuf_iterator<char>(in), istreambuf_iterator<char>());
}

int main(int argc, char* argv[]) {
    if (argc != 2) {
        cerr << "Usage: this_executable file_to_read\n";
        return EXIT_FAILURE;
    }
    cout << getfile(argv[1]).size() << "\n";
}

【讨论】:

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