繁体   English   中英

C ++在向量中加载大型txt文件的快速方法<string>

[英]C++ Fast way to load large txt file in vector<string>

我有一个文件〜12.000.000十六进制行和1,6GB文件的示例:

999CBA166262923D53D3EFA72F5C4E8EE1E1FF1E7E33C42D0CE8B73604034580F2
889CBA166262923D53D3EFA72F5C4E8EE1E1FF1E7E33C42D0CE8B73604034580F2

代码示例:

vector<string>  buffer;

ifstream fe1("strings.txt");
string line1;
    while (getline(fe1, line1)) {
        buffer.push_back(line1);
    }

现在加载大约需要20分钟。 有什么建议如何加快? 非常感谢。

将大型文本文件加载到std::vector<std::string>效率不高且浪费,因为它会为每个std::string分配堆内存并多次重新分配向量。 这些堆分配中的每一个都需要幕后的堆簿记信息(在64位系统上通常每个分配8字节) ,每行都需要一个std::string对象(8-32字节,取决于标准库),因此,以这种方式加载的文件在RAM中要比在磁盘上占用更多的空间。

一种快速的方法是将文件映射到内存中,并实现迭代器以遍历其中的行。 这回避了上述问题。

工作示例:

#include <boost/interprocess/file_mapping.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/range/iterator_range_core.hpp>

#include <iostream>

class LineIterator
    : public boost::iterator_facade<
          LineIterator,
          boost::iterator_range<char const*>,
          boost::iterators::forward_traversal_tag,
          boost::iterator_range<char const*>
          >
{
    char const *p_, *q_;
    boost::iterator_range<char const*> dereference() const { return {p_, this->next()}; }
    bool equal(LineIterator b) const { return p_ == b.p_; }
    void increment() { p_ = this->next(); }
    char const* next() const { auto p = std::find(p_, q_, '\n'); return p + (p != q_); }
    friend class boost::iterator_core_access;

public:
    LineIterator(char const* begin, char const* end) : p_(begin), q_(end) {}
};

inline boost::iterator_range<LineIterator> crange(boost::interprocess::mapped_region const& r) {
    auto p = static_cast<char const*>(r.get_address());
    auto q = p + r.get_size();
    return {LineIterator{p, q}, LineIterator{q, q}};
}

inline std::ostream& operator<<(std::ostream& s, boost::iterator_range<char const*> const& line) {
    return s.write(line.begin(), line.size());
}

int main() {
    boost::interprocess::file_mapping file("/usr/include/gnu-versions.h", boost::interprocess::read_only);
    boost::interprocess::mapped_region memory(file, boost::interprocess::read_only);

    unsigned n = 0;
    for(auto line : crange(memory))
        std::cout << n++ << ' ' << line;
}

您可以将整个文件读入内存。 这可以使用C ++流完成,或者您可以通过使用平台特定的API(例如,内存映射文件或它们自己的文件读取API)来获得更高的性能。

拥有此数据块后,为了提高性能,您要避免再复制并就地使用它。 在C ++ 17中,您具有std::string_view ,它与std::string相似,但是使用现有的字符串数据,从而避免了复制。 否则,您可能只使用C样式char*字符串,可以通过使用一对指针(开头/结尾)或指针和大小将换行替换为null( \\0 )。

在这里,我使用string_view ,我还假定换行符始终为\\n并且末尾有换行符。 如果不是这种情况,则可能需要调整循环。 猜测vector的大小也会获得一些性能,您可以通过文件长度来实现。 我也跳过了一些错误处理。

std::fstream is("data.txt", std::ios::in | std::ios::binary);
is.seekg(0, std::ios::end);
size_t data_size = is.tellg();
is.seekg(0, std::ios::beg);
std::unique_ptr<char[]> data(new char[data_size]);
is.read(data.get(), data_size);


std::vector<std::string_view> strings;
strings.reserve(data_size / 40); // If you have some idea, avoid re-allocations as general practice with vector etc.
for (size_t i = 0, start = 0; i < data_size; ++i)
{
    if (data[i] == '\n') // End of line, got string
    {
        strings.emplace_back(data.get() + start, i - start);
        start = i + 1;
    }
}

为了获得更高的性能,您可以运行循环以使CPU与文件IO并行运行。 这可以通过线程或特定于平台的异步文件IO来完成。 但是,在这种情况下,循环将非常快,因此收益不多。

您可以简单地分配足够的RAM内存并几乎一次读取整个文本文件。 比起您可以通过内存指针访问RAM中的数据。 我在3秒钟内阅读了整个4GB文本文件。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM