在NodeJS中读取第N行文件

Gle*_*min 23 node.js

我试图提取一个文件的一行,因为我知道路径名和行号,理想情况下我想这样做而不必读取任何文件而不是必要的.

出于我在这里使用的目的,无论是异步还是同步都无关紧要.

我当前(糟糕)的实现看起来像这样:

function get_line(filename, line_no, callback) {
    line_no = parseInt(line_no);
    var data = fs.readFileSync(filename, 'utf8');
    var lines = data.split("\n");
    for (var l in lines) {
        if (l == line_no - 1) {
            callback(null, lines[l].trim());
            return;
        }
    }
    throw new Error('File end reached without finding line');
}
Run Code Online (Sandbox Code Playgroud)

我试图用createReadStream做一些事情,但数据事件似乎永远不会触发.任何人都可以提供这个问题的直接解决方案,或者指向一些NodeJS文件系统交互文档,这些文档比标准库API文档更具实例性驱动?

FGR*_*eau 20

随着可读流

var fs = require('fs');

function get_line(filename, line_no, callback) {
    var stream = fs.createReadStream(filename, {
      flags: 'r',
      encoding: 'utf-8',
      fd: null,
      mode: 0666,
      bufferSize: 64 * 1024
    });

    var fileData = '';
    stream.on('data', function(data){
      fileData += data;

      // The next lines should be improved
      var lines = fileData.split("\n");

      if(lines.length >= +line_no){
        stream.destroy();
        callback(null, lines[+line_no]);
      }
    });

    stream.on('error', function(){
      callback('Error', null);
    });

    stream.on('end', function(){
      callback('File end reached without finding line', null);
    });

}

get_line('./file.txt', 1, function(err, line){
  console.log('The line: ' + line);
})
Run Code Online (Sandbox Code Playgroud)

直接解决方案

您应该使用切片方法而不是循环.

var fs = require('fs');

function get_line(filename, line_no, callback) {
    var data = fs.readFileSync(filename, 'utf8');
    var lines = data.split("\n");

    if(+line_no > lines.length){
      throw new Error('File end reached without finding line');
    }

    callback(null, lines[+line_no]);
}

get_line('./file.txt', 9, function(err, line){
  console.log('The line: ' + line);
})
Run Code Online (Sandbox Code Playgroud)

for(行中的var l)不是循环数组的最有效方法,你应该这样做:

for(var i = 0, iMax = lines.length; i < iMax; i++){/* lines[i] */ }
Run Code Online (Sandbox Code Playgroud)

异步方式:

var fs = require('fs');

function get_line(filename, line_no, callback) {
    fs.readFile(filename, function (err, data) {
      if (err) throw err;

      // Data is a buffer that we need to convert to a string
      // Improvement: loop over the buffer and stop when the line is reached
      var lines = data.toString('utf-8').split("\n");

      if(+line_no > lines.length){
        return callback('File end reached without finding line', null);
      }

      callback(null, lines[+line_no]);
    });
}

get_line('./file.txt', 9, function(err, line){
  console.log('The line: ' + line);
})
Run Code Online (Sandbox Code Playgroud)

  • 这些都没有实际读取内存中的任何文件 - 这是我正在搜索的优化.微优化javascript以从大字符串中提取行并不会在这里产生太大的影响我已经想过,因为IO仍然相对较慢.此外,您在异步版本中抛出异常,而不是简单地使用错误实例作为第一个参数调用回调. (5认同)

Gab*_*mas 6

不读取任何文件而不是必要的

编辑:模块没有维护,我建议使用其他模块逐行读取,例如,使用转换流:http://strongloop.com/strongblog/practical-examples-of-the-new-node-js-流的API /

使用BufferedReader:

var n = 10;
var l = null;

//Internally it uses a buffer, default 16KB, but you can reduce it to, for example, 4KB doing:
//new BufferedReader ("file", { encoding: "utf8", bufferSize: 4*1024 })

new BufferedReader ("file", { encoding: "utf8" })
    .on ("error", function (error){
        //error
        console.log (error);
    })
    .on ("line", function (line){
        if (!--n){
            l = line;
            //With interrupt you can stop the reading
            this.interrupt ();
        }
    })
    .on ("end", function (){
        //your Nth line!
        console.log (l);
    })
    .read ();
Run Code Online (Sandbox Code Playgroud)


Off*_*Off 5

您可以通过删除“fileData”变量中的先前数据来大大提高 FGRibreau 答案的性能。

function(file, line_no, cb){
var stream = fs.createReadStream(file, {
    flags: 'r',
    encoding: 'utf-8',
    fd: null,
    mode: '0666',
    bufferSize: 64 * 1024
});

var fileData = '';
stream.on('data', function(data){
    fileData += data;

    var lines = fileData.split('\n');

    if(lines.length >= +line_no){
        stream.destroy();
        cb(null, lines[+line_no]);
    }
    // Add this else condition to remove all unnecesary data from the variable
    else
        fileData = Array(lines.length).join('\n');

});

stream.on('error', function(){
    cb('Error', null);
});

stream.on('end', function(){
    cb('File end reached without finding line', null);
});
};
Run Code Online (Sandbox Code Playgroud)

使用 70000 行文件,为了显示第 n°50000 行,我得到了这些结果:

真正的 0m3.504s

用户 0m0.000s

系统 0m0.015s

对于与 else 相同的示例,我得到以下信息:

真实 0m0.540s

用户 0m0.015s

系统 0m0.031s

这也意味着内存消耗要低得多。