Javascript 在Node.JS中一次读取N行大文件

Javascript 在Node.JS中一次读取N行大文件,javascript,arrays,node.js,Javascript,Arrays,Node.js,我有一个65000000行的文件,大约2gb大小 我想一次读取N行中的文件,执行db insert操作,然后读取下一个N,在本例中,N是1000。插入顺序无关紧要,所以同步是可以的 最好的方法是什么?我只发现要么一次加载一行,要么将整个文件读入内存。下面的示例代码,我一直在使用它一次读取一行文件: var singleFileParser = (file, insertIntoDB) => { var lr = new LineByLineReader(file); lr.

我有一个65000000行的文件,大约2gb大小

我想一次读取N行中的文件,执行db insert操作,然后读取下一个N,在本例中,N是1000。插入顺序无关紧要,所以同步是可以的

最好的方法是什么?我只发现要么一次加载一行,要么将整个文件读入内存。下面的示例代码,我一直在使用它一次读取一行文件:

var singleFileParser = (file, insertIntoDB) => {
    var lr = new LineByLineReader(file);
    lr.on('error', function(err) {
        // 'err' contains error object
        console.error(err);
        console.error("Error reading file!");
    });

    lr.on('line', function(line) {
        insertIntoDB(line);
    // 'line' contains the current line without the trailing newline character.
    });

    lr.on('end', function() {
        // All lines are read, file is closed now.
    });
};

像这样的事情应该可以

var cnt = 0;
var tenLines = [];
lr.on('line', function(line) {
    tenLines.push(line);
    if (++cnt >= 10) {
         lr.pause();
         // prepare your SQL statements from tenLines
         dbInsert(<yourSQL>, function(error, returnVal){
            cnt = 0;
            tenLines = [];
            lr.resume();
        });
     }
});
var cnt=0;
var tenLines=[];
lr.on(线路),功能(线路){
十行。推(行);
如果(++cnt>=10){
lr.pause();
//从十行中准备SQL语句
dbInsert(,函数(错误,returnVal){
cnt=0;
十行=[];
lr.resume();
});
}
});

某个人一次只能解析一行。所以,如果你一次想要10个,那么你只需要一次收集一个,直到你收集了10个,然后处理10个

我不认为Jarek的代码工作得很好,所以这里有一个不同的版本,它将10行数据收集到一个数组中,然后调用
dbInsert()

var tenLines=[];
lr.on(线路),功能(线路){
十行。推(行);
如果(tenLines.length==10){
lr.pause();
dbInsert(,函数(错误,returnVal){
如果(错误){
//这里有些错误处理
}
十行=[];
lr.resume();
});
}
});
//处理十行缓冲区中的最后一组行(如果有)
lr.on('end',function(){
if(tenLines.length!==0){
//处理最后一组行
dbInsert(…);
}
});

Jarek的版本似乎对每一个
事件调用
dbInsert()
,而不仅仅是每10行事件调用一次,如果它们不是10行的完美倍数,则不会处理文件末尾的任何剩余行。

这是我在异步函数中的解决方案:

let multipleLines = [];
const filepath = '<file>';
const numberLines = 50;

const lineReader = require('readline').createInterface({
    input: require('fs').createReadStream(filepath)
});

// process lines by numberLines
for await (const line of lineReader) {
    multipleLines.push(line);
    if (multipleLines.length === numberLines) {
        await dbInsert();
        multipleLines = [];
    }
}
// process last set of lines (if any)
if (multipleLines.length !== 0) {
    await dbInsert();
}
let multipleline=[];
常量文件路径=“”;
常数数线=50;
const lineReader=require('readline')。createInterface({
输入:require('fs')。createReadStream(文件路径)
});
//数字线工艺生产线
用于等待(读线器的常量行){
多线推(线);
if(multipleLines.length==numberLines){
等待dbInsert();
多线=[];
}
}
//处理最后一组行(如果有)
if(multipleLines.length!==0){
等待dbInsert();
}

您是否考虑过编写一个小脚本,将文件分成更小的部分?只是一个想法。@JonLuca-Yep,所有的行都必须由某人一次解析一行,所以如果你想在执行数据库操作之前解析10行,那么只需收集行,直到有10行,然后再执行数据库操作。太好了,工作得非常好!真不敢相信我竟然没有想到这一点。还在考虑线程安全,忘记了节点是单线程的。谢谢@乔卢卡-嗯。我认为这是一个比赛条件。调用
dbInsert()
时,可能是在将来某个时候调用完成回调之前。这意味着其他(…)上的
lr.on
事件可以流动。然后,完成回调将到达,您将扰乱cnt和tenLines阵列。在开始db操作之前,您需要获取您的十行(可能将它们复制到另一个数组中),并将cnt设置回零,将数组设置回空。我看不到这一点。我假设调用
lr.pause()
时,在调用
lr.on(…)
之前,不会在
lr.resume()
上触发任何事件。所以,下次你看到一行时,它应该在
cnt=0之后;十行=[]已在回调中执行。让我想想。好吧,我没有看到
lr.pause()
(这是一种半隐藏的编码方式)。但是,如何不在每个
事件上调用
dbInsert()
?您不需要一个
if
语句,它在
cnt==10
时只调用
dbInsert()
?是的,它有这个问题。我做了和你一样的改变。谢谢@JonLuca-我做了额外的编辑,因为前面的代码没有处理最后一组行,除非它恰好是10的整数倍。我还删除了
cnt
变量,因为它不是必需的,因为我们可以使用
tenLines.length
作为计数器。
let multipleLines = [];
const filepath = '<file>';
const numberLines = 50;

const lineReader = require('readline').createInterface({
    input: require('fs').createReadStream(filepath)
});

// process lines by numberLines
for await (const line of lineReader) {
    multipleLines.push(line);
    if (multipleLines.length === numberLines) {
        await dbInsert();
        multipleLines = [];
    }
}
// process last set of lines (if any)
if (multipleLines.length !== 0) {
    await dbInsert();
}