65

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

使用 Python,我可以编写:

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

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

4

5 回答 5

135

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

读入 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>());

进入向量<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 即可。

于 2008-10-12T11:06:34.907 回答
12

关于这个主题还有另一个线程。

我从这个线程的解决方案(都是单行):

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

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

和快速:

string str(static_cast<stringstream const&>(stringstream() << ifs.rdbuf()).str());
于 2008-10-12T14:36:49.460 回答
4

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

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

祝一切顺利

/罗伯特

于 2008-10-12T11:06:02.257 回答
2

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

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

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

所以,如果你从一个未知来源的文件中读取,你应该:
- 检查它的大小并确保它小于 max_size()
- 捕获并处理 bad_alloc-exceptions

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

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

于 2010-09-23T11:41:07.353 回答
0

我喜欢 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";
}
于 2008-10-12T14:24:11.467 回答