C ++在关键字段上加入两个管道划分的文件
Posted
技术标签:
【中文标题】C ++在关键字段上加入两个管道划分的文件【英文标题】:C++ Join two pipe divided files on key fields 【发布时间】:2013-10-12 00:44:30 【问题描述】:我目前正在尝试创建一个 C++ 函数来连接两个管道分割文件,在一个或两个关键字段上具有超过 10.000.000 条记录。
字段看起来像
P2347|John Doe|C1234
P7634|Peter Parker|D2344
P522|Toni Stark|T288
和
P2347|Bruce Wayne|C1234
P1111|Captain America|D534
P522|Terminator|T288
要加入字段 1 和 3,预期输出应显示:
P2347|C1234|John Doe|Bruce Wayne
P522|T288|Toni Stark|Terminator
我目前考虑的是使用集合/数组/向量来读取文件并创建类似的内容:
P2347|C1234>>John Doe
P522|T288>>Toni Stark
和
P2347|C1234>>Bruce Wayne
P522|T288>>Terminator
然后使用slip第一部分作为key,与第二个set/vector/array进行匹配。
我目前拥有的是:读入第一个文件并逐行匹配第二个文件。它需要整行并匹配它:
#include iostream>
#include fstream>
#include string>
#include set>
#include ctime>
using namespace std;
int main()
clock_t startTime = clock();
ifstream inf("test.txt");
set lines;
string line;
for (unsigned int i=1; std::getline(inf,line); ++i)
lines.insert(line);
ifstream inf2("test2.txt");
clock_t midTime = clock();
ofstream outputFile("output.txt");
while (getline(inf2, line))
if (lines.find(line) != lines.end())
outputFile > a;
return 0;
我很高兴有任何建议。如果有更好(更快)的方法,我也很乐意改变整个概念。速度至关重要,因为可能有超过 1000 万条记录。
编辑:另一个想法是获取一张地图并让钥匙成为钥匙 - 但这可能会慢一点。有什么建议吗?
非常感谢您的帮助!
【问题讨论】:
通常你会得到按关键字段排序的输入文件,然后你不需要将整个文件加载到内存中。 第一步我可以这样做,但这意味着我必须阅读和写入两次。 你必须用 C++ 来做吗?使用awk
更容易做到这一点
必须是 C++,但感谢您指出这一点。
如果输入没有排序,则需要将文件加载到内存中。如果你需要做一个笛卡尔积,那么使用 multimap 来支持重复键。
【参考方案1】:
我尝试了多种方法来完成这项任务,但到目前为止都没有效率:
将所有内容读入一个集合并将关键字段解析为格式:keys >> values 模拟数组类型集合。解析花费了很长时间,但内存使用率保持相对较低。未完全开发的代码:
#include \
#include \
#include \
#include \
#include \
#include \
#include \
std::vector &split(const std::string &s, char delim, std::vector &elems)
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim))
elems.push_back(item);
return elems;
std::vector split(const std::string &s, char delim)
std::vector elems;
split(s, delim, elems);
return elems;
std::string getSelectedRecords(std::string record, int position)
std::string values;
std::vector tokens = split(record, ' ');
//get position in vector
for(auto& s: tokens)
//pick last one or depending on number, not developed
values = s;
return values;
int main()
clock_t startTime = clock();
std::ifstream secondaryFile("C:/Users/Batman/Desktop/test/secondary.txt");
std::set secondarySet;
std::string record;
for (unsigned int i=1; std::getline(secondaryFile,record); ++i)
std::string keys = getSelectedRecords(record, 2);
std::string values = getSelectedRecords(record, 1);
secondarySet.insert(keys + ">>>" + values);
clock_t midTime = clock();
std::ifstream primaryFile("C:/Users/Batman/Desktop/test/primary.txt");
std::ofstream outputFile("C:/Users/Batman/Desktop/test/output.txt");
while (getline(primaryFile, record))
//rewrite find() function to go through set and find all keys (first part until >> ) and output values
std::string keys = getSelectedRecords(record, 2);
if (secondarySet.find(keys) != secondarySet.end())
outputFile > a;
return 0;
目前它使用空间划分而不是管道划分,但这应该不是问题。读取数据非常快,但是解析需要很多时间
另一个选项是使用多地图。类似的概念,关键字段指向值,但这个概念非常低且占用大量内存。
#include \
#include \
#include \
#include \
#include \
#include \
#include \
int main()
std::clock_t startTime = clock();
std::ifstream inf("C:/Users/Batman/Desktop/test/test.txt");
typedef std::multimap Map;
Map map;
std::string line;
for (unsigned int i=1; std::getline(inf,line); ++i)
//load tokens into vector
std::istringstream buffer(line);
std::istream_iterator beg(buffer), end;
std::vector tokens(beg, end);
//get keys
for(auto& s: tokens)
//std::cout >>" second;
outputFile > a;
return 0;
进一步的想法是:在导入数据时将管道分割的文件分成不同的文件,每列一列。这样我就不必解析任何内容,但可以单独读取每一列。
编辑:使用递归拆分函数优化了第一个示例。对于 100.000 条记录,仍然 > 30 秒。希望看到更快加上实际的 find() 函数仍然缺失。
有什么想法吗? 谢谢!
【讨论】:
以上是关于C ++在关键字段上加入两个管道划分的文件的主要内容,如果未能解决你的问题,请参考以下文章