我想std::string
用 c++ 将文本文件的全部内容读取到对象中。
使用 Python,我可以编写:
text = open("text.txt", "rt").read()
它非常简单优雅。我讨厌丑陋的东西,所以我想知道 - 用 C++ 读取文本文件的最优雅的方法是什么?谢谢。
有很多方法,你选择最适合你的。
读入 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 即可。
关于这个主题还有另一个线程。
我从这个线程的解决方案(都是单行):
好的(见米兰的第二个解决方案):
string str((istreambuf_iterator<char>(ifs)), istreambuf_iterator<char>());
和快速:
string str(static_cast<stringstream const&>(stringstream() << ifs.rdbuf()).str());
您似乎将优雅称为“小代码”的明确属性。这当然在某种程度上是主观的。有人会说省略所有错误处理不是很优雅。有人会说,您立即理解的清晰紧凑的代码是优雅的。
编写您自己的读取文件内容的单行函数/方法,但在表面下使其严格和安全,您将涵盖优雅的两个方面。
祝一切顺利
/罗伯特
但请注意,一个 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() 的一次调用!只需谷歌搜索“令牌迭代器”,您就会明白我的意思。所以在我看来,这比仅仅从文件中读取更“优雅”......
我喜欢 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";
}