c++ - 在 C++ 中,从大型文本文件集合中读取所有单词的最快方法是什么?
问题描述
我有一个大文本文件的集合,我希望我的代码逐字读取它们并将单词存储在字符串变量中。我编写了一个简单的代码作为原型,它从文件集合中读取单词并打印它们(实际代码没有打印命令),但我的实际程序将包含大约 20 个文件和数十万个单词。看来这段代码的性能不会很好。有什么可以提高速度的吗?
#include <iostream>
#include <fstream>
using namespace std;
int main() {
string s;
string array[3]={"text1.txt","text2.txt","text3.txt"};
for(int i=0;i<3;i++){
ifstream fileread;
fileread.open(array[i]);
while(fileread>>s){
cout<<s<<endl;
}
}
return 0;
}
解决方案
您已经将单词放入字符串变量中,最有可能减慢速度的是将实际单词打印到std::cout
.
如果你有一个非常快的磁盘(比如 ramdisk),理论上你可以通过一次读取多个文件来加快速度。
我已经提出了一个想法(需要 C++17 或更高版本)如何做到这一点,它还测量它所花费的时间并在读取完成时显示一些统计数据。我已经在代码中进行了注释以解释它的作用。要与串行读取文件时的速度进行比较,只需std::execution::par
删除std::for_each
.
#include <algorithm>
#include <chrono>
#include <cstddef>
#include <execution>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>
int cppmain(const std::string_view program, std::vector<std::string> files) {
if(files.empty()) {
std::cerr << "USAGE: " << program << " <files...>\n";
return 1;
}
// measure how long time it takes to read all the files
auto start = std::chrono::steady_clock::now();
// a map of filenames mapping to a vector of words in each file
std::unordered_map<std::string, std::vector<std::string>> file_words_map;
// create the filename Keys in the map - this is needed to not insert keys
// in the map object itself from multiple threads simultaneously.
for(auto& file : files) file_words_map[file];
// make sure the files vector only contains unique filenames
if(files.size() != file_words_map.size()) {
// we had duplicate filenames - remove them
files.resize(file_words_map.size());
std::transform(file_words_map.begin(), file_words_map.end(),
files.begin(),
[](auto& file_words) { return file_words.first; });
}
// read from multiple files simultaneously
std::for_each(std::execution::par, files.begin(), files.end(),
[&file_words_map](auto& file) {
std::ifstream is(file); // open a file for reading
if(is) { // and check that it opened ok
// get a reference to this file's vector in the map
auto& words = file_words_map[file];
// copy all words from the file into the words vector
std::copy(std::istream_iterator<std::string>(is),
std::istream_iterator<std::string>{},
std::back_inserter(words));
}
});
// all files read, calculate how long time it took
auto elapsed_time = std::chrono::steady_clock::now() - start;
// calculate how many words we read in total
auto total =
std::accumulate(file_words_map.begin(), file_words_map.end(), 0ULL,
[](const auto val, const auto& file_words) {
return val + file_words.second.size();
});
// statistics
constexpr std::size_t max_words_to_display = 5;
for(const auto& [file, words] : file_words_map) {
std::cout << std::setw(50) << std::left << file << ' ' << std::setw(10)
<< std::right << words.size() << "\n ";
std::copy_n(words.begin(), std::min(max_words_to_display, words.size()),
std::ostream_iterator<std::string>(std::cout, ", "));
if(words.size() > max_words_to_display) std::cout << "...";
std::cout << '\n';
}
std::cout << "Read " << total << " words from " << file_words_map.size()
<< " files in "
<< std::chrono::duration_cast<std::chrono::milliseconds>(elapsed_time)
.count()
<< " ms.\n";
return 0;
}
int main(int argc, char* argv[]) {
return cppmain(argv[0], {argv + 1, argv + argc});
}
示例输出:
...
handler.cpp 155
#include, <condition_variable>, #include, <functional>, #include, ...
pybind.cpp 162
#include, <iostream>, #include, <pybind11.h>, #include, ...
readip.cpp 296
#include, <netinet/ip.h>, #include, <fstream>, #include, ...
realdist.cpp 299
#include, <cmath>, #include, <iostream>, #include, ...
strerr.cpp 125
#include, <iostream>, using, namespace, std;, ...
rec.cpp 91
#include, <filesystem>, #include, <iostream>, #include, ...
Read 239715 words from 1621 files in 11 ms.
注意:您实际上可以直接执行for_each
循环file_words_map
而不是使用files
向量,然后words
像上面的代码那样在地图中查找向量 - 这会更干净,并且不需要从向量中删除重复项 -但对我来说,这种方法要快 4-5 倍,因为速度很重要,所以我使用了这个。您可以自己进行试验以查看结果。
如果您使用g++
or clang++
,请编译:
-O3 -std=c++17 -ltbb -pthread
在 MSVC 中,您不需要指定库。
推荐阅读
- flutter - How can i change the state of a widget from and external class
- codeigniter - 如何让 Codeigniter 在表格视图中显示所有条目而不限制为 10 行数据?
- xpath - eXist-db - XQuery - Lucene - controlling output in KWIC function with callback parameter
- ruby - 在纯红宝石中将 camelCase 转换为破折号(连字符)
- python - mypy 和 attrs:错误类型检查子类列表
- c++ - QT OpenSSL AES 256 CBC 加密程序在尝试写入文件时崩溃
- javascript - How to create a slider in a stack layout
- c# - C# do..while 循环列表
- vue.js - 如何在Vue中设置默认选择
- wpf - 如何在 WPF 中折叠星形网格列?