作为C++迭代器抽象和算法的忠实粉丝,我希望下面的方法能够成为读取文件(或任何其他输入流)并将其存储到
std::string
中(然后打印内容)的最快方式:
#include <algorithm>
#include <fstream>
#include <iostream>
#include <iterator>
#include <string>
int main()
{
std::string s(std::istreambuf_iterator<char>(std::ifstream("file")
>> std::skipws),
std::istreambuf_iterator<char>());
std::cout << "file='" << s << "'\n";
}
这对于我自己实现的IOStreams来说确实很快,但需要很多技巧才能真正做到快速。主要是需要优化算法来处理
分段序列:流可以看作是输入缓冲区的序列。我不知道任何STL实现会一直进行此优化。奇怪的
std::skipws
用法只是为了获取新创建的流的引用:因为
std::istreambuf_iterator<char>
需要一个引用,而临时文件流无法绑定。
由于这可能不是最快的方法,我倾向于使用std::getline()
并指定一个特定的“换行符”,例如在文件中不存在的字符:
std::string s;
std::getline(std::ifstream("file") >> std::skipws, s, 0);
这假设文件中没有空字符,任何其他字符也可以。不幸的是,
std::getline()
需要一个
char_type
作为定界参数,而不是成员
std::istream::getline()
所需的
int_type
:在这种情况下,您可以使用
eof()
表示永远不会出现的字符(
char_type
、
int_type
和
eof()
指的是
char_traits<char>
的相应成员)。反过来,成员版本不能使用,因为您需要事先知道文件中有多少个字符。
顺便说一句,我看到一些尝试使用寻找来确定文件大小的方法,但这样做效果不佳。问题在于在
std::ifstream
(实际上在
std::filebuf
)中执行的代码转换可能会产生与文件中的字节数不同的字符数。诚然,在使用默认C语言环境时,这种情况并不适用,并且可以检测到这不进行任何转换。否则,流的最佳选择是运行文件并确定正在生成的字符数。我认为,当代码转换可能会产生一些有趣的东西时,这实际上就是需要完成的工作,尽管我不认为它确实已经完成了。但是,没有一个示例明确设置了C语言环境,例如使用
std::locale::global(std::locale("C"));
。即使有这个设置,也需要以
std::ios_base::binary
模式打开文件,因为否则在读取时可能会将行尾序列替换为单个字符。诚然,这只会使结果更短,从不会更长。
其他使用从
std::streambuf*
(即涉及
rdbuf()
的那些)提取的方法都要求在某个时刻复制结果内容。考虑到文件实际上可能非常大,这可能不是一个选项。没有复制,这可能是最快的方法。但是,为了避免复制,可以创建一个简单的自定义流缓冲区,该流缓冲区以
std::string
的引用作为构造函数参数,并直接附加到此
std::string
中:
#include <fstream>
#include <iostream>
#include <string>
class custombuf:
public std::streambuf
{
public:
custombuf(std::string& target): target_(target) {
this->setp(this->buffer_, this->buffer_ + bufsize - 1);
}
private:
std::string& target_;
enum { bufsize = 8192 };
char buffer_[bufsize];
int overflow(int c) {
if (!traits_type::eq_int_type(c, traits_type::eof()))
{
*this->pptr() = traits_type::to_char_type(c);
this->pbump(1);
}
this->target_.append(this->pbase(), this->pptr() - this->pbase());
this->setp(this->buffer_, this->buffer_ + bufsize - 1);
return traits_type::not_eof(c);
}
int sync() { this->overflow(traits_type::eof()); return 0; }
};
int main()
{
std::string s;
custombuf sbuf(s);
if (std::ostream(&sbuf)
<< std::ifstream("readfile.cpp").rdbuf()
<< std::flush) {
std::cout << "file='" << s << "'\n";
}
else {
std::cout << "failed to read file\n";
}
}
使用适当选择的缓冲区,我预计版本会非常快。哪个版本最快肯定取决于系统、所使用的标准C++库以及可能的其他因素,即你需要测量性能。
char*
来读取,然后使用string(const char * s, size_t n)
构造函数呢? - akappachar*
)的形式高效地访问大文件,你可能需要查看一下mmap。 - Aaron McDaid