从文件

时间:2017-03-24 18:00:58

标签: c++ performance file newline

简介

我有一个名为MyProcess的C ++流程,我称之为nbLines次,其中nbLines是一个名为InputDataFile.txt的大文件的行数,其中输入数据为被发现。例如,电话

./MyProcess InputDataFile.txt 142

通知MyProcess输入数据可在142文件的InputDataFile.txt行找到。

问题

问题是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);

目标

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

可能的解决方案

将每行第一个字符的索引与bash中的行号匹配会很方便。这样,我可以直接给出第一个感兴趣字符的索引,而不是将142赋予MyProcessMyProcess然后可以直接跳转到此位置,而无需搜索和计算&#39; \ n&#39;字符。然后它将读取数据直到&#39; \ n&#39;性格遭遇。这样的事情可行吗?怎么可以实现呢?

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

3 个答案:

答案 0 :(得分:2)

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

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

现在你有一个&#34; Map&#34;文件是64位整数的列表(文件中每行一个)。要阅读地图,您只需在地图中计算所需行所在的条目:

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)。然后,您只需通过计算存储行号偏移的位置的偏移量并读取该偏移量以及下一行偏移量来搜索映射文件。从那里你可以得到数据所在位置的数字范围。

示例:

数据:

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

创建地图。

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 base 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;
}

答案 1 :(得分:1)

没有“快速”方法来读取文件的第N个文本行。

文本文件包含可变长度记录。每条记录都以换行符结束。必须逐个字符地阅读文本,直到找到换行符。这可以是1个字符,也可以是245个字符。没有标准尺寸。

通常的做法是读取每一行并忽略该行,直到找到所需的行。

如果您经常需要转到文件中的特定行,则可以维护行号的地图及其文件位置。

否则,您可以尝试将块或块读入缓冲区并扫描缓冲区。这将加速您的程序,但您必须考虑可能跨越缓冲区边界的文本行。请记住,输入在保持流式传输时最有效(想想数据流)。

答案 2 :(得分:0)

因为它标有class Machine,所以这是一个带bash的简单函数

定义

sed

使用

getline() { sed "${2}q;d" "$1"; }