c++ - 从文件中读取第 n 行的快速方法

标签 c++ performance file newline

简介

我有一个名为 MyProcess 的 C++ 进程,我调用了 nbLines 次,其中 nbLines 是名为 InputDataFile.txt 在其中找到输入数据。例如调用

./MyProcess InputDataFile.txt 142

通知 MyProcess 输入数据位于 InputDataFile.txt 文件的 142 行。

问题

问题是 InputDataFile.txt 太大(~ 150 GB)以至于搜索正确行的时间不可忽略。启发形式this post ,这是我的(可能不是最优的)代码

int line = 142;
int N = line - 1;
std::ifstream inputDataFile(filename.c_str());
std::string inputData;
for(int i = 0; i < N; ++i)
    std::getline(inputDataFile, inputData);

std::getline(inputDataFile,inputData);

目标

我的目标是为 MyProcess 更快地搜索 inputData

可能的解决方案

bash 中将每行的第一个字符的索引与行号匹配一次会很方便。这样,我就可以直接给出第一个感兴趣的字符的索引,而不是将 142 提供给 MyProcessMyProcess 可以直接跳转到这个位置,而无需搜索和计算 '\n' 字符。然后它将读取数据,直到遇到“\n”字符。这样的事情可行吗?如何实现?

当然,我欢迎任何其他可以减少导入这些输入数据的总体计算时间的解决方案。

最佳答案

正如其他答案中所建议的那样,构建文件的 map 可能是个好主意。我这样做的方式(伪代码)是:

let offset be a unsigned 64 bit int =0;

for each line in the file 
    read the line
    write offset to a binary file (as 8 bytes rather as chars)
    offset += length of line in bytes

现在您有一个“ map ”文件,它是一个 64 位整数列表(文件中的每一行一个)。要阅读 map ,您只需计算您想要的线路条目在 map 中的位置:

offset = desired_line_number * 8 // where line number starts at 0
offset2 = (desired_line_number+1) * 8

data_position1 = load bytes [offset through offset + 8] as a 64bit int from map
data_position2 = load bytes [offset2 through offset2 + 8] as a 64bit int from map

data = load bytes[data_position1 through data_position2-1] as a string from data.

这个想法是,您读取一次数据文件并在文件中每行开始的位置记录字节偏移量,然后使用固定大小的整数类型将偏移量顺序存储在二进制文件中。映射文件的大小应为 number_of_lines * sizeof(integer_type_used)。然后,您只需通过计算存储行号偏移量的偏移量并读取该偏移量以及下一行偏移量来查找 map 文件。从那里你有一个数字范围,以字节为单位,说明你的数据应该位于何处。

例子:

数据:

hello\n 
world\n
(\n newline at end of file)

创建 map 。

Map:每个分组[number]代表文件中的一个8字节长度

[0][7][14]
//or in binary
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000111
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00001110

现在说我想要第 2 行:

line offset = 2-1 * 8 // offset is 8 

因此,由于我们使用的是基数为 0 的系统,因此它是文件中的第 9 个字节。所以输出数字由字节 9 - 17 组成,它们是:

00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000111
//or as decimal
7

现在我们知道 out 行应该从数据文件中的偏移量 7 开始(这个偏移量以 1 为底,如果我们从 0 开始计数,它就是 6)。

然后我们执行相同的过程以获得下一行的起始偏移量,即 14。

最后我们查找字节范围 7-14(基数 1、6-13 基数 0)并将其存储为字符串并获取 world\n

C++ 实现:

#include <iostream>
#include <fstream>

int main(int argc, const char * argv[]) {
    std::string filename = "path/to/input.txt";

    std::ifstream inputFile(filename.c_str(),std::ios::binary);
    std::ofstream outfile("path/to/map/file.bin",std::ios::binary|std::ios::ate);

    if (!inputFile.is_open() || !outfile.is_open()) {
        //use better error handling than this
        throw std::runtime_error("Error opening files");
    }


    std::string inputData;
    std::size_t offset = 0;
    while(std::getline(inputFile, inputData)){
        //write the offset as binary
        outfile.write((const char*)&offset, sizeof(offset));
        //increment the counter
        offset+=inputData.length()+2;
        //add one becuase getline strips the \n and add one to make the index represent the next line
    }
    outfile.close();

    offset=0;

    //from here on we are reading the map
    std::ifstream inmap("/Users/alexanderzywicki/Documents/xcode/textsearch/textsearch/map",std::ios::binary);
    std::size_t line = 2;//your chosen line number
    std::size_t idx = (line-1) * sizeof(offset); //the calculated offset
    //seek into the map
    inmap.seekg(idx);
    //read the binary at that location
    inmap.read((char*)&offset, sizeof(offset));
    std::cout<<offset<<std::endl;

    //from here you just need to lookup from the data file in the same manor


    return 0;
}

关于c++ - 从文件中读取第 n 行的快速方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43006281/

相关文章:

Java 读取对象并将其写入文件

database - 在 Firefox 附加组件中保存数据的最佳方法

c++ - 线程体系结构问题 C++ 消息传递

c++ - 如何获取当前流程的工作对象(如果有)?

performance - 从表存储读取期间 CPU 使用率高

performance - Haswell内存访问

c++ - 录制应用程序的音频输出

c++ - 减少代码重复并用模板替换#define

滚动控制台窗口时 Node.js 停止

file - 需要在golang中生成运行时二进制编码文件