javascript - 処理 - readlines js




node.jsで一度に1行ずつファイルを読み込みますか? (17)

一度に大きなファイルを1行ずつ読み込もうとしています。 私は、問題を扱ったQuoraに関する質問を見つけましが、私はすべてのものが合うようにいくつかのつながりを失っています。

 var Lazy=require("lazy");
 new Lazy(process.stdin)
     .lines
     .forEach(
          function(line) { 
              console.log(line.toString()); 
          }
 );
 process.stdin.resume();

私が把握したいのは、このサンプルのようにSTDINの代わりにファイルから一度に1行を読み込む方法です。

私は試した:

 fs.open('./VeryBigFile.csv', 'r', '0666', Process);

 function Process(err, fd) {
    if (err) throw err;
    // DO lazy read 
 }

しかし、それは動作していません。 私はちょっとしたことで、PHPのようなものに落ちる可能性があることを知っていますが、これを理解したいと思います。

私は他の答えは、ファイルが私がそれを実行しているサーバーよりもはるかに大きいので、メモリを持っているとは思わない。


Node.js v0.12以降、Node.js v4.0.0以降、安定したreadlineコアモジュールがあります。 外部モジュールなしで、ファイルから行を読み込む最も簡単な方法は次のとおりです。

var lineReader = require('readline').createInterface({
  input: require('fs').createReadStream('file.in')
});

lineReader.on('line', function (line) {
  console.log('Line from file:', line);
});

最終行が正しくない場合でも、最後の行は正しく読み取られます(ノードv0.12以降)。

UPDATE :この例は、NodeのAPI公式ドキュメントに追加されています


キャリアモジュールの場合

var carrier = require('carrier');

process.stdin.resume();
carrier.carry(process.stdin, function(line) {
    console.log('got one line: ' + line);
});

このような簡単な操作のために、第三者のモジュールに依存するべきではありません。 簡単に行く。

var fs = require('fs'),
    readline = require('readline');

var rd = readline.createInterface({
    input: fs.createReadStream('/path/to/file'),
    output: process.stdout,
    console: false
});

rd.on('line', function(line) {
    console.log(line);
});

ほとんどの場合、これで十分です。

const fs = require("fs")

fs.readFile('./file', 'utf-8', (err, file) => {
  const lines = file.split('\n')

  for (let line of lines)
    console.log(line)
});


ファイルを1行ずつ読み込んで別のファイルに書きたい場合は、次のようにします。

var fs = require('fs');
var readline = require('readline');
var Stream = require('stream');

function readFileLineByLine(inputFile, outputFile) {

   var instream = fs.createReadStream(inputFile);
   var outstream = new Stream();
   outstream.readable = true;
   outstream.writable = true;

   var rl = readline.createInterface({
      input: instream,
      output: outstream,
      terminal: false
   });

   rl.on('line', function (line) {
        fs.appendFileSync(outputFile, line + '\n');
   });
};

ファイルをopen必要はありませんが、代わりにReadStreamを作成するReadStreamます。

fs.createReadStream

その後、そのストリームをLazy渡します


上の答えが示唆しているように、おそらくreadlineモジュールを使用するべきですが、 readlineはラインの読み込みではなくコマンドラインインターフェイスに向いているようです。 バッファリングに関しても少し不透明です。 (ストリーミングライン指向の読者が必要な人は、おそらくバッファサイズを微調整したいと思うでしょう)。 readlineモジュールは〜1000行で、統計とテストでは34です。

const EventEmitter = require('events').EventEmitter;
class LineReader extends EventEmitter{
    constructor(f, delim='\n'){
        super();
        this.totalChars = 0;
        this.totalLines = 0;
        this.leftover = '';

        f.on('data', (chunk)=>{
            this.totalChars += chunk.length;
            let lines = chunk.split(delim);
            if (lines.length === 1){
                this.leftover += chunk;
                return;
            }
            lines[0] = this.leftover + lines[0];
            this.leftover = lines[lines.length-1];
            if (this.leftover) lines.pop();
            this.totalLines += lines.length;
            for (let l of lines) this.onLine(l);
        });
        // f.on('error', ()=>{});
        f.on('end', ()=>{console.log('chars', this.totalChars, 'lines', this.totalLines)});
    }
    onLine(l){
        this.emit('line', l);
    }
}
//Command line test
const f = require('fs').createReadStream(process.argv[2], 'utf8');
const delim = process.argv[3];
const lineReader = new LineReader(f, delim);
lineReader.on('line', (line)=> console.log(line));

統計情報がない場合でも、19行でさらに短いバージョンが表示されます。

class LineReader extends require('events').EventEmitter{
    constructor(f, delim='\n'){
        super();
        this.leftover = '';
        f.on('data', (chunk)=>{
            let lines = chunk.split(delim);
            if (lines.length === 1){
                this.leftover += chunk;
                return;
            }
            lines[0] = this.leftover + lines[0];
            this.leftover = lines[lines.length-1];
            if (this.leftover) 
                lines.pop();
            for (let l of lines)
                this.emit('line', l);
        });
    }
}

私の元の回答を投稿してから、 splitはファイル内の行の読み込みに非常に使いやすいノードモジュールであることがわかりました。 オプションのパラメータも受け入れます。

var split = require('split');
fs.createReadStream(file)
    .pipe(split())
    .on('data', function (line) {
      //each chunk now is a seperate line! 
    });

非常に大きなファイルではテストしていません。 もしそうなら、私たちに知らせてください。


私はこのための包括的な解決策の欠如に不満を抱いていたので、私は自分の試み( git / npm )をまとめました。 コピーして貼り付けた機能のリスト:

  • インタラクティブなライン処理(コールバックベース、ファイル全体をRAMにロードしない)
  • オプションで、配列内のすべての行を返す(詳細モードまたはrawモード)
  • インタラクティブにストリーミングを中断したり、マップ/フィルタのような処理を実行する
  • 改行規則を検出する(PC / Mac / Linux)
  • eof /最終行の処置を修正する
  • マルチバイトUTF-8文字の正しい処理
  • 行単位でバイトオフセットとバイト長の情報を取得する
  • 行ベースまたはバイトベースのオフセットを使用したランダムアクセス
  • ランダムアクセスを高速化するために、ラインオフセット情報を自動的にマップする
  • ゼロの依存関係
  • テスト

NIH? あなたが決める :-)


私はこれをうまくやっている他のいくつかのプロジェクトで使用されている小さなモジュールを持っています。readline#v10ノードにはネイティブのreadlineモジュールがありますので、私のモジュールをラインバインとして再公開しましたhttps://www.npmjs.com/package/linebyline //www.npmjs.com/package/ https://www.npmjs.com/package/linebyline

あなたがモジュールを使いたくない場合、関数は非常に簡単です:

var fs = require('fs'),
EventEmitter = require('events').EventEmitter,
util = require('util'),
newlines = [
  13, // \r
  10  // \n
];
var readLine = module.exports = function(file, opts) {
if (!(this instanceof readLine)) return new readLine(file);

EventEmitter.call(this);
opts = opts || {};
var self = this,
  line = [],
  lineCount = 0,
  emit = function(line, count) {
    self.emit('line', new Buffer(line).toString(), count);
  };
  this.input = fs.createReadStream(file);
  this.input.on('open', function(fd) {
    self.emit('open', fd);
  })
  .on('data', function(data) {
   for (var i = 0; i < data.length; i++) {
    if (0 <= newlines.indexOf(data[i])) { // Newline char was found.
      lineCount++;
      if (line.length) emit(line, lineCount);
      line = []; // Empty buffer.
     } else {
      line.push(data[i]); // Buffer new line data.
     }
   }
 }).on('error', function(err) {
   self.emit('error', err);
 }).on('end', function() {
  // Emit last line if anything left over since EOF won't trigger it.
  if (line.length){
     lineCount++;
     emit(line, lineCount);
  }
  self.emit('end');
 }).on('close', function() {
   self.emit('close');
 });
};
util.inherits(readLine, EventEmitter);

私はこれを使う:

function emitLines(stream, re){
    re = re && /\n/;
    var buffer = '';

    stream.on('data', stream_data);
    stream.on('end', stream_end);

    function stream_data(data){
        buffer += data;
        flush();
    }//stream_data

    function stream_end(){
        if(buffer) stream.emmit('line', buffer);
    }//stream_end


    function flush(){
        var re = /\n/;
        var match;
        while(match = re.exec(buffer)){
            var index = match.index + match[0].length;
            stream.emit('line', buffer.substring(0, index));
            buffer = buffer.substring(index);
            re.lastIndex = 0;
        }
    }//flush

}//emitLines

この関数をストリーム上で使用し、出力する行イベントを待ち受けます。

gr-


私は以下のコードを読んだ後に、ディレクトリではなく、ファイルのリストに含まれていないことを確認する必要がないことを確認してから、コードを使用します。

(function () {
  var fs = require('fs');
  var glob = require('glob-fs')();
  var path = require('path');
  var result = 0;
  var exclude = ['LICENSE',
    path.join('e2e', 'util', 'db-ca', 'someother-file'),
    path.join('src', 'favicon.ico')];
  var files = [];
  files = glob.readdirSync('**');

  var allFiles = [];

  var patternString = [
    'trade',
    'order',
    'market',
    'securities'
  ];

  files.map((file) => {
    try {
      if (!fs.lstatSync(file).isDirectory() && exclude.indexOf(file) === -1) {
        fs.readFileSync(file).toString().split(/\r?\n/).forEach(function(line){
          patternString.map((pattern) => {
            if (line.indexOf(pattern) !== -1) {
              console.log(file + ' contain `' + pattern + '` in in line "' + line +'";');
              result = 1;
            }
          });
        });
      }
    } catch (e) {
      console.log('Error:', e.stack);
    }
  });
  process.exit(result);

})();

私は大量の大量のメモリリークにhttp://elegantcode.com/2011/04/06/taking-baby-steps-with-node-js-pumping-data-between-streams/ 、ノードの動作中のドレイン/ポーズ/レジュームのためにそれらの行を処理して別のストリームに書き込もうとしました。( http://elegantcode.com/2011/04/06/taking-baby-steps-with-node-js-pumping-data-between-streams/ (私はこの男が大好きです。 私はLazyで十分な理由を正確に理解していませんでしたが、Lazyを終了させずに排水を許可するために読書ストリームを一時停止できませんでした。

私は大量のcsvファイルをxmlドキュメントに処理するコードを書いています。ここでコードを見ることができます: https://github.com/j03m/node-csv2xml : https://github.com/j03m/node-csv2xml

以前のリビジョンをLazyラインで実行すると、リークします。 最新のリビジョンはまったくリークしませんし、おそらくそれをリーダー/プロセッサーの基礎として使うことができます。 私はそこにカスタム品を持っていますが。

編集:必要があるため、ドレイン/ポーズ/レジュームする十分な大きさのXML断片を書くことがわかってしまうまで、Lazyのコードがうまく動作することにも注意してください。 小さなチャンクの場合は問題ありませんでした。


const fs = require("fs")

fs.readFile('./file', 'utf-8', (err, data) => {
var innerContent;
    console.log("Asynchronous read: " + data.toString());
    const lines = data.toString().split('\n')
    for (let line of lines)
        innerContent += line + '<br>';


});

function createLineReader(fileName){
    var EM = require("events").EventEmitter
    var ev = new EM()
    var stream = require("fs").createReadStream(fileName)
    var remainder = null;
    stream.on("data",function(data){
        if(remainder != null){//append newly received data chunk
            var tmp = new Buffer(remainder.length+data.length)
            remainder.copy(tmp)
            data.copy(tmp,remainder.length)
            data = tmp;
        }
        var start = 0;
        for(var i=0; i<data.length; i++){
            if(data[i] == 10){ //\n new line
                var line = data.slice(start,i)
                ev.emit("line", line)
                start = i+1;
            }
        }
        if(start<data.length){
            remainder = data.slice(start);
        }else{
            remainder = null;
        }
    })

    stream.on("end",function(){
        if(null!=remainder) ev.emit("line",remainder)
    })

    return ev
}


//---------main---------------
fileName = process.argv[2]

lineReader = createLineReader(fileName)
lineReader.on("line",function(line){
    console.log(line.toString())
    //console.log("++++++++++++++++++++")
})

var fs = require('fs');

function readfile(name,online,onend,encoding) {
    var bufsize = 1024;
    var buffer = new Buffer(bufsize);
    var bufread = 0;
    var fd = fs.openSync(name,'r');
    var position = 0;
    var eof = false;
    var data = "";
    var lines = 0;

    encoding = encoding || "utf8";

    function readbuf() {
        bufread = fs.readSync(fd,buffer,0,bufsize,position);
        position += bufread;
        eof = bufread ? false : true;
        data += buffer.toString(encoding,0,bufread);
    }

    function getLine() {
        var nl = data.indexOf("\r"), hasnl = nl !== -1;
        if (!hasnl && eof) return fs.closeSync(fd), online(data,++lines), onend(lines); 
        if (!hasnl && !eof) readbuf(), nl = data.indexOf("\r"), hasnl = nl !== -1;
        if (!hasnl) return process.nextTick(getLine);
        var line = data.substr(0,nl);
        data = data.substr(nl+1);
        if (data[0] === "\n") data = data.substr(1);
        online(line,++lines);
        process.nextTick(getLine);
    }
    getLine();
}

私は同じ問題を抱えていて、上記の解決策は他の人に似ているように見えますが、aSyncであり、大きなファイルをすばやく読み取ることができます

これが役立つことが期待されます





lazy-evaluation