我有一个文件〜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分钟。有什么建议如何加快?提前谢谢。
答案 0 :(得分:2)
将大型文本文件加载到std::vector<std::string>
中是相当低效和浪费的,因为它会为每个std::string
分配堆内存,并多次重新分配向量。这些堆分配中的每一个都需要在(normally 8 bytes per allocation on a 64-bit system)下进行堆簿记信息,并且每一行都需要一个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;
}
答案 1 :(得分:1)
您可以将整个文件读入内存。可以使用C ++流完成此操作,也可以通过使用平台特定的API(例如内存映射文件或它们自己的文件读取API)来获得更高的性能。
拥有此数据块后,为了提高性能,您希望避免再复制并就地使用。在C ++ 17中,您有std::string_view
,它与std::string
类似,但是使用现有的字符串数据,从而避免了复制。否则,您可能只使用C样式的char*
字符串,可以通过使用一对指针(开头/结尾)或指针和大小来将换行符替换为空(\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来完成。但是,在这种情况下,循环将非常快,因此不会有太多收获。
答案 2 :(得分:0)
您可以简单地分配足够的RAM内存并几乎一次读取整个文本文件。比起您可以通过内存指针访问RAM中的数据。我在大约3秒钟内阅读了整个4GB的文本文件。