node.jsで一度に1行ずつファイルを読み取りますか?


553

大きなファイルを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のようなものにフォールバックできることを知っていますが、これを理解したいと思います。

私が実行しているサーバーがメモリを持っているサーバーよりもファイルが非常に大きいため、他の答えはうまくいかないと思います。


2
これは、低レベルのを使用するだけではかなり難しいことがわかりますfs.readSync()。バイナリオクテットをバッファに読み込むことはできますが、バッファをJavaScript文字列に変換してEOLをスキャンする前にバッファを検査せずに、部分的なUTF-8またはUTF-16文字を処理する簡単な方法はありません。Buffer()タイプは、ネイティブ文字列としてそのインスタンスを操作する機能の豊富なセットとして持っていませんが、ネイティブの文字列はバイナリデータを含めることはできません。任意のファイルハンドルからテキスト行を読み取る組み込みの方法がないことは、node.jsの実際のギャップのように思えます。
ヒッピートレイル2013年

5
このメソッドによって読み込まれた空の行は、0(実際には0の文字コード)が1つ含まれる行に変換されます。私はそこにこの行をハックしなければなりませんでした:if (line.length==1 && line[0] == 48) special(line);
Thabo

2
完全に機能する 'line-by-line'パッケージを使用することもできます。
Patrice

1
質問を更新して、解決策が変換ストリーム
Gabriel Llamas 2014年

2
これをリストに追加できる場合は、@ DanDascalescu:例は、nodeAPIのドキュメントgithub.com/nodejs/node/pull/4609
eljefedelrodeodeljefe

回答:


790

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

const fs = require('fs');
const readline = require('readline');

async function processLineByLine() {
  const fileStream = fs.createReadStream('input.txt');

  const rl = readline.createInterface({
    input: fileStream,
    crlfDelay: Infinity
  });
  // Note: we use the crlfDelay option to recognize all instances of CR LF
  // ('\r\n') in input.txt as a single line break.

  for await (const line of rl) {
    // Each line in input.txt will be successively available here as `line`.
    console.log(`Line from file: ${line}`);
  }
}

processLineByLine();

または代わりに:

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

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

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

更新:この例は、NodeのAPI公式ドキュメントに追加れました


7
createInterface定義にterminal:falseが必要です
glasspill

64
最後の行を決定する方法は?「クローズ」イベントをキャッチすることによって:rl.on('close', cb)
グリーン

27
ReadlineはGNU Readlineと同様の目的であり、ファイルを1行ずつ読み取るためのものではありません。これを使用してファイルを読み取る場合、いくつかの注意点がありますが、これはベストプラクティスではありません。
2015年

8
@Nakedible:面白い。より良い方法で回答を投稿できますか?
Dan Dascalescu、2015年

6
私はgithub.com/jahewson/node-bylineを1 行ずつ読むのに最適な実装だと考えていますが、意見は異なる場合があります。
Nakedible

164

このような単純な操作では、サードパーティのモジュールに依存するべきではありません。簡単に。

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

33
残念なことに、この魅力的なソリューションは正しく機能しlineません\n。つまり、イベントはを押した後にのみ発生します。#2、最後の後のデータ\nは黙って無視されます(stackoverflow.com/questions/18450197/…を参照)。私はこのソリューションを危険と呼びます。すべてのファイルの99%とデータの99%で機能しますが、残りの部分では黙っ失敗します。いつでもfs.writeFileSync( path, lines.join('\n'))、上記のソリューションで部分的にしか読み取られないファイルを書き込んだことになります。
フロー

4
このソリューションには問題があります。your.js <lines.txtを使用すると、最後の行が表示されません。もちろん、最後に「\ n」がない場合。
zag2art 2014年

readline経験豊富なUNIX / Linuxのプログラマに本当に奇妙な方法でパッケージ振る舞います。
2014年

11
rd.on("close", ..);コールバックとして使用できます(すべての行が読み取られたときに発生します)
Luca Steeb

6
「最後の\ nの後のデータ」の問題は、私のバージョンのノード(0.12.7)で解決されるようです。だから私はこの答えを好みます。これは最も単純で最もエレガントなようです。
Myk Melez

63

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

fs.createReadStream

次に、そのストリームを Lazy


2
Lazyの終了イベントのようなものはありますか?すべての行が読み込まれたとき?
最大

1
@Max、Try:new lazy(fs.createReadStream('...')).lines.forEach(function(l) { /* ... */ }).join(function() { /* Done */ })
Cecchi

6
@Cecchiと@Maxは、ファイル全体をメモリにバッファリングするため、ジョインを使用しないでください。代わりに、単に「終了」イベントに耳を傾ける:new lazy(...).lines.forEach(...).on('end', function() {...})
コリン

3
@ Cecchi、@ Corin、および@Max:その価値のために、私は最初にイベントをバインドしたときに実際にすべてが期待どおりに動作した.on('end'... .forEach(...)、自分をクレイジーチェーンしました。
crowjonah 2013

52
この結果は検索結果で非常に高いため、Lazyが放棄されたように見えることは注目に値します。変更なしで7か月が経過し、恐ろしいバグがいくつかあります(最後の行は無視された、大量のメモリリークなど)。
ブルー

38

行ごとにファイルを読み取るための非常に優れたモジュールがあり、それはラインリーダーと呼ばれています

それであなたは単に書くだけです:

var lineReader = require('line-reader');

lineReader.eachLine('file.txt', function(line, last) {
  console.log(line);
  // do whatever you want with line...
  if(last){
    // or check if it's the last one
  }
});

さらに制御が必要な場合は、「javaスタイル」インターフェースでファイルを反復することもできます。

lineReader.open('file.txt', function(reader) {
  if (reader.hasNextLine()) {
    reader.nextLine(function(line) {
      console.log(line);
    });
  }
});

4
これはうまくいきます。最後の行(!)も読み取ります。Windowsスタイルのテキストファイルの場合、\ rを保持することは言及に値します。line.trim()は余分な\ rを削除するトリックを行います。
Pierre-Luc Bertrand

入力は名前付きファイルからのみであり、(明確で非常に重要な例としてはprocess/stdin)ではないという点で、最適ではありません。少なくとも、可能であれば、コードを読んで試してみるのは明らかではありません。
2014年

2
それまでの間、readlineコアモジュールを使用して、ファイルから行を読み取る組み込みの方法があります
Dan Dascalescu、2015

これは古いですが、誰かがそれにつまずいた場合に備えて:function(reader)function(line)すべきです:function(err,reader)function(err,line)
jallmer 2018年

1
念のため、line-readerファイルを非同期で読み取ります。それに同期し代替があるline-reader-sync
Prajwal Dhatwalia

30
require('fs').readFileSync('file.txt', 'utf-8').split(/\r?\n/).forEach(function(line){
  console.log(line);
})

42
これにより、メモリ内のファイル全体が読み取られ、それが行に分割されます。それは質問が尋ねるものではありません。重要なのは、必要に応じて大きなファイルを順番に読み取ることができることです。
Dan Dascalescu 2013年

2
これは私のユースケースに適合し、あるスクリプトからの入力を別の形式に変換する簡単な方法を探していました。ありがとう!
-Callat

23

2019年に更新

すばらしい例は、公式のNodejsドキュメントに既に投稿されています。ここに

これには、最新のNodejsがマシンにインストールされている必要があります。> 11.4

const fs = require('fs');
const readline = require('readline');

async function processLineByLine() {
  const fileStream = fs.createReadStream('input.txt');

  const rl = readline.createInterface({
    input: fileStream,
    crlfDelay: Infinity
  });
  // Note: we use the crlfDelay option to recognize all instances of CR LF
  // ('\r\n') in input.txt as a single line break.

  for await (const line of rl) {
    // Each line in input.txt will be successively available here as `line`.
    console.log(`Line from file: ${line}`);
  }
}

processLineByLine();

EOFを明確に示す約束ベースの動作により、この回答は上記のどれよりもはるかに優れています。
phil294

ありがとう、それは甘い。
Goran Stoyanov

3
多分これは他の人には明らかですが、デバッグに少し時間がかかりました:呼び出しとループの開始awaitの間にsがある場合、不思議なことにファイルの先頭から行が失われます。すぐに背後でラインの放出を開始します。暗黙的に作成された非同期イテレータは、作成されるまでこれらのラインのリスニングを開始できません。createInterface()for awaitcreateInterface()const line of rl
andrewdotn

19

古いトピックですが、これは機能します:

var rl = readline.createInterface({
      input : fs.createReadStream('/path/file.txt'),
      output: process.stdout,
      terminal: false
})
rl.on('line',function(line){
     console.log(line) //or parse line
})

シンプル。外部モジュールは必要ありません。


2
readline is not definedまたはを取得した場合fs is not definedは、var readline = require('readline');およびvar fs = require('fs');を追加してこれを機能させます。そうでなければ、甘い、甘いコード。ありがとう。
bergie3000 2015

12
この回答は以前の回答まったく同じですが、コメントの警告なしでreadlineパッケージは不安定(2015年4月の時点でも不安定)とマークされており、2013年半ばにファイルの最後の行を行末なしで読み取ることができませんでした。最後の行の問題は、v0.10.35で初めて使用したときに発生し、その後消えました。/
argh

ファイルストリームから読み取るだけの場合は、出力を指定する必要はありません。
Dan Dascalescu、2015

18

いつでも自分のラインリーダーをロールできます。このスニペットのベンチマークはまだ行っていませんが、チャンクの着信ストリームを末尾の「\ n」なしで行に正しく分割します

var last = "";

process.stdin.on('data', function(chunk) {
    var lines, i;

    lines = (last+chunk).split("\n");
    for(i = 0; i < lines.length - 1; i++) {
        console.log("line: " + lines[i]);
    }
    last = lines[i];
});

process.stdin.on('end', function() {
    console.log("line: " + last);
});

process.stdin.resume();

ログ解析中にデータを蓄積する必要のあるクイックログ解析スクリプトで作業しているときにこれを思いつきました。perlやbashを使用する代わりに、jsとnodeを使用してこれを試すのが良いと感じました。

とにかく、小さなnodejsスクリプトは自己完結型であり、サードパーティのモジュールに依存しないようにすべきだと思うので、この質問に対するすべての回答を読んだ後、それぞれがさまざまなモジュールを使用して行の解析を処理した後、13 SLOCネイティブのnodejsソリューションが興味深いかもしれません。


これを拡張して任意のファイルで動作するようにする簡単な方法はないようですstdin
ヒッピートレイル2013年

3
@hippietrail withを作成しReadStreamfs.createReadStream('./myBigFile.csv')、代わりに使用できますstdin
nolith

2
各チャンクは完全な行のみを含むことが保証されていますか?マルチバイトUTF-8文字はチャンクの境界で分割されないことが保証されていますか?
ヒッピートレイル2013年

1
@hippietrailこの実装ではマルチバイト文字が正しく処理されるとは思いません。そのためには、まずバッファを文字列に正しく変換し、2つのバッファ間で分割される文字を追跡する必要があります。それを適切に行うには、組み込みのStringDecoderを
Ernelli

それまでの間、readlineコアモジュールを使用して、ファイルから行を読み取る組み込みの方法があります
Dan Dascalescu、2015

12

キャリアモジュール

var carrier = require('carrier');

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

いいね。これはまた、任意の入力ファイルに対して動作します。var inStream = fs.createReadStream('input.txt', {flags:'r'}); しかし、あなたの構文は(.on使用しての文書化方法よりもクリーン)である:carrier.carry(inStream).on('line', function(line) { ...
ブレント・ファウスト

キャリアは\r\n\n行末のみを処理するようです。OS X以前のMacOSスタイルのテストファイルを処理する必要がある場合、それらは使用されて\rおり、キャリアはこれを処理しません。驚いたことに、まだそのようなファイルが野生に浮かんでいます。Unicode BOM(バイトオーダーマーク)を明示的に処理する必要がある場合もあります。これは、MS Windowsの影響範囲内のテキストファイルの先頭で使用されます。
ヒッピートレイル

それまでの間、readlineコアモジュールを使用して、ファイルから行を読み取る組み込みの方法があります
Dan Dascalescu、2015

9

ノードでのドレイン/一時停止/再開の仕組みが原因で、それらの行を処理して別のストリームに書き込もうとすると、Lazyを使用して1行ずつ読み取り、大量の大規模なメモリリークが発生しました(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

以前のリビジョンをレイジーラインで実行するとリークします。最新のリビジョンはまったくリークしないので、おそらくリーダー/プロセッサーのベースとして使用できます。私はそこにいくつかのカスタムのものを持っていますが。

編集:必要に応じてドレイン/一時停止/再開する十分な大きさのxmlフラグメントを作成するまで、Lazyを使用したコードは問題なく機能したことにも注意する必要があります。小さなチャンクの場合は問題ありませんでした。


それまでの間、readlineコアモジュールを使用して、ファイルから行を読み取るより簡単な方法があります
Dan Dascalescu、2015

うん。これが正しい方法です。しかし、これは2011年からでした。:)
j03m

8

編集:

変換ストリームを使用します


BufferedReaderのあなたは、ラインを読むことができます。

new BufferedReader ("lorem ipsum", { encoding: "utf8" })
    .on ("error", function (error){
        console.log ("error: " + error);
    })
    .on ("line", function (line){
        console.log ("line: " + line);
    })
    .on ("end", function (){
        console.log ("EOF");
    })
    .read ();

1
それまでの間、readlineコアモジュールを使用して、ファイルから行を読み取るより簡単な方法があります
Dan Dascalescu、2015

7

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

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

非常に大きなファイルではテストしていません。その場合はお知らせください。


6

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

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

NIH(アメリカ国立衛生研究所)(#文字数制限がない場合、初出時にかっこ書きを追加?あなたが決める :-)


5
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("++++++++++++++++++++")
})

私はこれをテストしますが、マルチバイト文字を壊さないことが保証されていますか?(UTF-8 / UTF-16)
ヒッピートレイル2013年

2
@hippietrail:文字ストリームではなくバイトストリームを処理している場合でも、UTF-8の場合、答えはノーです。改行(0x0a)で中断します。UTF-8では、マルチバイト文字のすべてのバイトに高位ビットが設定されています。したがって、マルチバイト文字には、埋め込まれた改行やその他の一般的なASCII文字を含めることができません。ただし、UTF-16とUTF-32は別の問題です。
ジョージ

@ジョージ:私たちはお互いを誤解していると思います。CRとLFはどちらもASCII範囲内にあり、UTF-8は128文字のASCII文字を変更せずに保持するため、CRもLFもマルチバイトUTF-8文字の一部になることはできません。私が求めていたことかどうかであるdataへの呼び出し中stream.on("data")かもしれない、これまでのようなマルチバイトのUTF-8文字の一部だけで始めまたは終わりU+10D0、3バイトで構成e1 83 90
ヒッピー・トレイル

1
これは、「新しい行」にする前に、ファイルの内容全体をメモリにロードします。これは一度に1行ずつ読み取るのではなく、代わりにすべての行を取得して、「新しい行」のバッファー長に従って分割します。このメソッドは、ストリームを作成する目的を無効にします。
ジャスティン

それまでの間、readlineコアモジュールを使用して、ファイルから行を読み取るより簡単な方法があります
Dan Dascalescu、2015

5

私はこれと同じ問題に取り組みたかったのですが、基本的にはPerlではどうなるでしょうか。

while (<>) {
    process_line($_);
}

私の使用例は、サーバーではなくスタンドアロンスクリプトだけだったので、同期は問題ありませんでした。これらは私の基準でした:

  • 多くのプロジェクトで再利用できる最小限の同期コード。
  • ファイルサイズや行数に制限はありません。
  • 行の長さに制限はありません。
  • BMPを超える文字を含むUTF-8で完全なUnicodeを処理できます。
  • * nixとWindowsの行末を処理できます(旧式のMacは不要です)。
  • 行に含まれる行末文字。
  • 行末文字の有無にかかわらず最終行を処理できます。
  • node.jsディストリビューションに含まれていない外部ライブラリは使用しないでください。

これは、node.jsの低レベルのスクリプトタイプのコードの感触をつかんで、Perlのような他のスクリプト言語の代わりとしてどれほど実行可能かを判断するためのプロジェクトです。

驚くほどの労力といくつかの誤ったスタートの後、これは私が思いついたコードです。かなり高速ですが、思ったより簡単ではありません:(GitHubでフォークしてください)

var fs            = require('fs'),
    StringDecoder = require('string_decoder').StringDecoder,
    util          = require('util');

function lineByLine(fd) {
  var blob = '';
  var blobStart = 0;
  var blobEnd = 0;

  var decoder = new StringDecoder('utf8');

  var CHUNK_SIZE = 16384;
  var chunk = new Buffer(CHUNK_SIZE);

  var eolPos = -1;
  var lastChunk = false;

  var moreLines = true;
  var readMore = true;

  // each line
  while (moreLines) {

    readMore = true;
    // append more chunks from the file onto the end of our blob of text until we have an EOL or EOF
    while (readMore) {

      // do we have a whole line? (with LF)
      eolPos = blob.indexOf('\n', blobStart);

      if (eolPos !== -1) {
        blobEnd = eolPos;
        readMore = false;

      // do we have the last line? (no LF)
      } else if (lastChunk) {
        blobEnd = blob.length;
        readMore = false;

      // otherwise read more
      } else {
        var bytesRead = fs.readSync(fd, chunk, 0, CHUNK_SIZE, null);

        lastChunk = bytesRead !== CHUNK_SIZE;

        blob += decoder.write(chunk.slice(0, bytesRead));
      }
    }

    if (blobStart < blob.length) {
      processLine(blob.substring(blobStart, blobEnd + 1));

      blobStart = blobEnd + 1;

      if (blobStart >= CHUNK_SIZE) {
        // blobStart is in characters, CHUNK_SIZE is in octets
        var freeable = blobStart / CHUNK_SIZE;

        // keep blob from growing indefinitely, not as deterministic as I'd like
        blob = blob.substring(CHUNK_SIZE);
        blobStart -= CHUNK_SIZE;
        blobEnd -= CHUNK_SIZE;
      }
    } else {
      moreLines = false;
    }
  }
}

それはおそらくさらにクリーンアップされる可能性があり、試行錯誤の結果でした。


5

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

const fs = require("fs")

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

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

2

ジェネレーターベースのラインリーダー:https : //github.com/neurosnap/gen-readlines

var fs = require('fs');
var readlines = require('gen-readlines');

fs.open('./file.txt', 'r', function(err, fd) {
  if (err) throw err;
  fs.fstat(fd, function(err, stats) {
    if (err) throw err;

    for (var line of readlines(fd, stats.size)) {
      console.log(line.toString());
    }

  });
});

2

ファイルを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');
   });
};

あなたとコフラサの答えの違いは何ですか?
バッファロー

2
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であり、大きなファイルを非常にすばやく読み取ることができます

これが役に立てば幸い


1

私はこれをうまく実行し、他のかなりの数のプロジェクトで使用される小さなモジュールを持っていますnpm readlineノードv10にはネイティブのreadlineモジュールがあるので、linebyline https://www.npmjs.com/package/ 1行ずつ

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

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);

1

別のソリューションは、シーケンシャルエグゼキューターnsynjsを介してロジックを実行することです。ノードのreadlineモジュールを使用してファイルを1行ずつ読み取り、promiseや再帰を使用しないため、大きなファイルで失敗することはありません。コードは次のようになります。

var nsynjs = require('nsynjs');
var textFile = require('./wrappers/nodeReadline').textFile; // this file is part of nsynjs

function process(textFile) {

    var fh = new textFile();
    fh.open('path/to/file');
    var s;
    while (typeof(s = fh.readLine(nsynjsCtx).data) != 'undefined')
        console.log(s);
    fh.close();
}

var ctx = nsynjs.run(process,{},textFile,function () {
    console.log('done');
});

上記のコードはこの例に基づいています:https : //github.com/amaksr/nsynjs/blob/master/examples/node-readline/index.js


1

そのような操作をしているときに私たちが自問しなければならない2つの質問は、

  1. それを実行するために使用されるメモリの量はどれくらいですか?
  2. ファイルのサイズに応じてメモリ消費量が大幅に増加していますか?

などのソリューションrequire('fs').readFileSync()は、ファイル全体をメモリにロードします。つまり、操作を実行するために必要なメモリの量は、ファイルサイズとほぼ同じになります。これより大きなものについては、これらを回避する必要があります50mbs

これらのコード行を関数呼び出しの後に配置することで、関数が使用するメモリ量を簡単に追跡できます。

    const used = process.memoryUsage().heapUsed / 1024 / 1024;
    console.log(
      `The script uses approximately ${Math.round(used * 100) / 100} MB`
    );

現在、大きなファイルから特定の行を読み取る最善の方法は、ノードのreadlineを使用することです。ドキュメントにはすばらしい例があります。

ただし、サードパーティのモジュールは必要ありません。ただし、エンタープライズコードを記述している場合は、多数のエッジケースを処理する必要があります。Apick File Storageという非常に軽量なモジュールを作成する必要がありましたこれらすべてのエッジケースを処理するに。

Apickファイルストレージモジュール:https ://www.npmjs.com/package/apickfs ドキュメント:https : //github.com/apickjs/apickFS#readme

サンプルファイル:https : //1drv.ms/t/s!AtkMCsWInsSZiGptXYAFjalXOpUx

例:モジュールのインストール

npm i apickfs
// import module
const apickFileStorage = require('apickfs');
//invoke readByLineNumbers() method
apickFileStorage
  .readByLineNumbers(path.join(__dirname), 'big.txt', [163845])
  .then(d => {
    console.log(d);
  })
  .catch(e => {
    console.log(e);
  });

この方法は、最大4 GBの高密度ファイルで正常にテストされました。

big.textは、163,845行の高密度テキストファイルで、124 Mbです。このファイルから10行を読み取るスクリプトは、約4.63 MBのメモリのみを使用します。また、有効なJSONをオブジェクトまたは配列に無料で解析します。🥳すごい!!

メモリをほとんど消費せずに、ファイルの1行または数百行を読み取ることができます。


0

私はこれを使います:

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-


0

おそらく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);
        });
    }
}

0
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>';


});


-1

ディレクトリではなく、ファイルのリストに含まれていないことを確認した後、以下のコードを使用して読み取り行をチェックします。

(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);

})();

-1

私は上記のすべての回答を調べましたが、それらはすべてサードパーティのライブラリを使用して解決しています。NodeのAPIにはシンプルなソリューションがあります。例えば

const fs= require('fs')

let stream = fs.createReadStream('<filename>', { autoClose: true })

stream.on('data', chunk => {
    let row = chunk.toString('ascii')
}))
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.