Nodejsで大きなJSONファイルを解析する 質問する

Nodejsで大きなJSONファイルを解析する 質問する

多くの JavaScript オブジェクトを JSON 形式で保存するファイルがあり、そのファイルを読み取り、各オブジェクトを作成し、それらに対して何らかの操作 (私の場合は DB に挿入) を行う必要があります。JavaScript オブジェクトは次の形式で表すことができます。

フォーマットA:

[{name: 'thing1'},
....
{name: 'thing999999999'}]

またはフォーマットB:

{name: 'thing1'}         // <== My choice.
...
{name: 'thing999999999'}

は多くの JSON オブジェクトを示していることに注意してください...。ファイル全体をメモリに読み込んで、JSON.parse()次のように使用できることはわかっています。

fs.readFile(filePath, 'utf-8', function (err, fileContents) {
  if (err) throw err;
  console.log(JSON.parse(fileContents));
});

ただし、ファイルが非常に大きくなる可能性があるので、これを実現するにはストリームを使用することをお勧めします。ストリームの問題は、ファイルの内容が任意の時点でデータ チャンクに分割される可能性があることです。JSON.parse()このようなオブジェクトではどのように使用すればよいでしょうか。

理想的には、各オブジェクトは別々のデータチャンクとして読み込まれるはずですが、どうやってするか

var importStream = fs.createReadStream(filePath, {flags: 'r', encoding: 'utf-8'});
importStream.on('data', function(chunk) {

    var pleaseBeAJSObject = JSON.parse(chunk);           
    // insert pleaseBeAJSObject in a database
});
importStream.on('end', function(item) {
   console.log("Woot, imported objects into the database!");
});*/

注意: ファイル全体をメモリに読み込まないようにしたいのです。時間効率は私にとって重要ではありません。確かに、一度に複数のオブジェクトを読み込んで、一度にすべて挿入することはできますが、それはパフォーマンスの調整です。ファイルに含まれるオブジェクトの数に関係なく、メモリのオーバーロードが発生しないことが保証される方法が必要です。

FormatAまたは、あるいは他の何かを使用することを選択できますFormatB。回答で具体的に指定してください。ありがとうございます!

ベストアンサー1

ファイルを行ごとに処理するには、ファイルの読み取りとその入力に基づいて動作するコードを分離するだけです。これは、改行に達するまで入力をバッファリングすることで実現できます。1 行につき 1 つの JSON オブジェクトがあると仮定します (基本的に、形式 B)。

var stream = fs.createReadStream(filePath, {flags: 'r', encoding: 'utf-8'});
var buf = '';

stream.on('data', function(d) {
    buf += d.toString(); // when data is read, stash it in a string buffer
    pump(); // then process the buffer
});

function pump() {
    var pos;

    while ((pos = buf.indexOf('\n')) >= 0) { // keep going while there's a newline somewhere in the buffer
        if (pos == 0) { // if there's more than one newline in a row, the buffer will now start with a newline
            buf = buf.slice(1); // discard it
            continue; // so that the next iteration will start with data
        }
        processLine(buf.slice(0,pos)); // hand off the line
        buf = buf.slice(pos+1); // and slice the processed data off the buffer
    }
}

function processLine(line) { // here's where we do something with a line

    if (line[line.length-1] == '\r') line=line.substr(0,line.length-1); // discard CR (0x0D)

    if (line.length > 0) { // ignore empty lines
        var obj = JSON.parse(line); // parse the JSON
        console.log(obj); // do something with the data here!
    }
}

ファイル ストリームがファイル システムからデータを受信するたびに、そのデータはバッファーに格納され、pump呼び出されます。

バッファに改行がない場合、pump何もせずに単に戻ります。ストリームが次にデータを取得するときに、さらにデータ (および場合によっては改行) がバッファに追加され、完全なオブジェクトが作成されます。

改行がある場合、pumpバッファの先頭から改行までを切り取って に渡しますprocess。次に、バッファ内に別の改行があるかどうかを再度確認します (ループwhile)。このようにして、現在のチャンクで読み取られたすべての行を処理できます。

最後に、process入力行ごとに 1 回呼び出されます。存在する場合は、復帰文字を削除し (行末の問題 (LF と CRLF) を回避するため)、JSON.parse行を呼び出します。この時点で、オブジェクトに対して必要な操作をすべて実行できます。

は入力として受け入れるものに関して厳格であることに注意してくださいJSON.parse。識別子と文字列値は引用符で囲む必要があります。二重引用符付きつまり、{name:'thing1'}はエラーをスローするので、 を使用する必要があります{"name":"thing1"}

一度にメモリに格納されるのはデータのチャンクのみなので、メモリ効率が非常に高くなります。また、処理速度も非常に速くなります。簡単なテストでは、15 ミリ秒未満で 10,000 行を処理できました。

おすすめ記事