c++ - 将文件读入 std::string 的最有效方法是什么?

标签 c++ string file-io

我目前这样做,最后转换为 std::string 占用了 98% 的执行时间。一定有更好的方法!

std::string
file2string(std::string filename)
{
    std::ifstream file(filename.c_str());
    if(!file.is_open()){
        // If they passed a bad file name, or one we have no read access to,
        // we pass back an empty string.
        return "";
    }
    // find out how much data there is
    file.seekg(0,std::ios::end);
    std::streampos length = file.tellg();
    file.seekg(0,std::ios::beg);
    // Get a vector that size and
    std::vector<char> buf(length);
    // Fill the buffer with the size
    file.read(&buf[0],length);
    file.close();
    // return buffer as string
    std::string s(buf.begin(),buf.end());
    return s;
}

最佳答案

作为 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>需要临时文件流不会绑定(bind)的引用。

因为这可能不是最快的方法,我倾向于使用 std::getline()具有特定的“换行符”字符,即文件中不存在的字符:

std::string s;
// optionally reserve space although I wouldn't be too fuzzed about the
// reallocations because the reads probably dominate the performances
std::getline(std::ifstream("file") >> std::skipws, s, 0);

这假设文件不包含空字符。任何其他角色也可以。不幸的是,std::getline()需要 char_type作为定界参数,而不是 int_type这是什么成员(member)std::istream::getline()用作分隔符:在这种情况下,您可以使用 eof()对于从未出现的字符( char_typeint_typeeof() 指的是 char_traits<char> 的相应成员)。反过来,成员(member)版本不能使用,因为您需要提前知道文件中有多少字符。

顺便说一句,我看到一些尝试使用 seeking 来确定文件的大小。这注定不会奏效。问题是在 std::ifstream 中完成的代码转换(好吧,实际上在 std::filebuf 中)可以创建与文件中的字节数不同的字符数。不可否认,使用默认 C 语言环境时情况并非如此,并且可以检测到这没有进行任何转换。否则,流的最佳选择是遍历文件并确定生成的字符数。我实际上认为,当代码转换可能是有趣的事情时,这是需要完成的,尽管我认为它实际上并没有完成。但是,没有一个示例明确设置 C 语言环境,例如使用std::locale::global(std::locale("C")); .即使这样,也有必要打开 std::ios_base::binary 中的文件。 mode 因为否则行尾序列在阅读时可能会被单个字符替换。不可否认,这只会使结果更短,而不会更长。

使用从 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++ 库以及可能的许多其他因素,即您要衡量性能。

关于c++ - 将文件读入 std::string 的最有效方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8736862/

相关文章:

c++ - 如何使用 c++ 2010、2012 和 2013 工具集安装一个版本的 visual studio 2013

c++ - QT中的渲染循环函数在Windows中渲染opengl

java - 是否可以使用字符串作为计算?

c - 消失的弦

c - 如何在 C 中将 4 个数字打印为字符串?

c# - 使用 Protobuf-net 恢复损坏的文件序列化

Python - 从文件中读取第二列

c++ - 优化字符串数组中最宽字符串的查找?

c++ - 初始化和赋值

file-io - 使用 VBScript 在单个文件夹中查找最新文件日期