我有一个大文件(5亿条记录)。 该文件是两列(制表符分隔),如下所示:
1 4590
3 1390
4 4590
5 4285
7 8902
8 9000
...
第一列中的所有值都是按数字排序的(但是有间隙,例如:1然后是3而不是4 ......)。
我想索引该文件,以便能够根据第1列的值访问column2上的值(我将调用密钥)
例如,如果我提交8,则应返回9000。
我已经开始创建索引,如下所示:
// Record each entry into a structure
struct Record{
int gi; //first column
int taxa; //second column
};
Record buffer;
ofstream BinaryFile("large_file_indexed.bin", ios::binary);
ifstream inputFile("infile.dat");
//Write to binary file
while( inputFile.good() ){
inputFile >> buffer.gi >> buffer.taxa;
BinaryFile.write( (char *) &buffer, sizeof(Record) );
}
BinaryFile.close();
好的,我上面所做的只是为条目创建二进制索引文件并将其保存为二进制文件。这是按预期工作的。
现在出现了问题,因为我不是专家,所以我很感激你的意见。 想法是读取二进制文件并获取特定记录
//Read binary file
ifstream ReadBinary("large_file_indexed.bin, ios::binary );
int idx = 8 ; // Which key do we search for?
while(!ReadBinary.eof())
{
ReadBinary.read( (char *) &buffer, sizeof(Record));
if(idx == buffer.gi) // If we find key return corresponding value
{
cout << "Found key " << buffer.gi << " Taxa:" << buffer.taxa << endl;
break;
}
}
返回预期值。由于我们要求对应于键8的值,因此返回9000。
事情是,它仍然太长,无法获得价值,我想知道我怎样才能更快。如果我使用seekg并且可以获得特定索引但我不知道哪个索引(位置)对应于我们想要的密钥。所以换句话说,我可以直接跳转到键所在的位置并获得相应的值。我很困惑如何获取特定键的位置并跳转到二进制文件中的相应位置。也许我应该以不同的方式索引我的输入文件或者我错过了什么?
感谢您的评论。
答案 0 :(得分:2)
如果您不能使用数据库或b-tree库,并且不想投资开发另一个b-tree库,则可以考虑以下两种方法之一。
两者都假设二进制索引文件已排序,并利用固定大小的记录。
1.简单的启发式方法
如果没有差距,找到第n条记录(编号从1开始),你会这样做:
if (ReadBinary.seekg(sizeof(Record)*(n-1))
&& ReadBinary.read( (char*)&buffer, sizeof(Record))) {
// process record
}
else {
// record not found (certainly beyond eof)
}
但你可以有差距。这意味着,如果没有重复,则元素n将位于此位置或之前。所以只要有必要,就可以阅读和回放:
if (! ReadBinary.seekg(sizeof(Record)*(n-1))) { // try to position
ReadBinary.clear(); // if couldn't position
ReadBinary.seekg(-sizeof(Record), ios_base::end); // go to last record
}
while (ReadBinary.read( (char*)&buffer, sizeof(Record)) && buffer.gi>n ) {
ReadBinary.seekg (-2*sizeof(Record), ios_base::cur);
}
if (ReadBinary && buffer.gi==n) {
// record found
}
else {
// record not found
}
2.Dichotomic approach
当然,如果你有很多差距,这种启发式方法会很快变得太慢,因为搜索的数量会增加。
您可以选择dichotomic search(又名binary search):seekg()
转到文件末尾并使用tellg()
了解文件大小,你可以翻译成多少条记录。
将数字剪切成两个,位于中间记录上的位置,读取它,查看搜索到的数字是否小于或大于读取的数字,然后重新启动搜索的新界限,直到找到正确的位置。您将用于在数组中搜索的相同原则。
这非常有效,因为您只需要在大多数log(n)/ log(2)读取时找到任何数字。因此,对于5亿个数字中的任何一个,您最多需要29次读取!
<强> 3.Conclusions 强>
当然还有其他可行的方法。但最终,即使它被任何数据库或精心设计的b-tree库都超越,这已经相当不错了,因为b-tree通过将节点重新组合成优化到的块来减少磁头移动以最小的磁盘开销一次读取。这减少了对log(n)/ log(b)的磁盘访问次数,其中b是块中的节点数。例如,如果b = 10,搜索500 000 000个元素将需要最多9次从磁盘读取。