[解決済み] Nodejsで大きなJSONファイルをパースする
質問
私は多くのJavaScriptオブジェクトをJSON形式で保存したファイルを持っており、ファイルを読み、各オブジェクトを作成し、それらを使って何かをする必要があります(私の場合、それらをデータベースに挿入する)。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 つの 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
は入力行ごとに一度だけ呼ばれます。 もし存在すれば、キャリッジリターン文字を取り除き (行末の問題 - LF と CRLF を回避するため)、それから
JSON.parse
を一行呼び出す。 この時点で、オブジェクトに対して必要なことを何でも行うことができます。
注意してほしいのは
JSON.parse
は入力として受け付けるものに厳しいので、識別子と文字列値を引用符で囲む必要があります。
を二重引用符で囲む必要があります。
. 言い換えると
{name:'thing1'}
はエラーを投げるので、必ず
{"name":"thing1"}
.
一度にメモリ上に存在するのは1つのデータチャンクだけなので、メモリ効率は非常に高くなります。 また、非常に高速になります。 簡単なテストでは、10,000 行を 15ms 未満で処理しました。
関連
-
[解決済み] 正しいJSONコンテンツタイプは何ですか?
-
[解決済み] JSONでコメントを使用することはできますか?
-
[解決済み] あるJavaScriptファイルを他のJavaScriptファイルにインクルードするにはどうすればよいですか?
-
[解決済み] なぜGoogleはJSONレスポンスにwhile(1);を前置するのでしょうか?
-
[解決済み] cURLでJSONデータをPOSTするにはどうすればよいですか?
-
[解決済み] ファイルのコピー方法について教えてください。
-
[解決済み] JavaScriptでJSONをきれいに印刷する
-
[解決済み] React js 親コンポーネントから子コンポーネントの状態を変更する
-
[解決済み] CORS OriginヘッダーとCSRFトークンによるCSRF保護
-
[解決済み] SVG のテキスト要素の幅を取得する
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] 配列からオブジェクトを生成する
-
[解決済み] JavaScriptで:hoverのCSSプロパティを変更する
-
[解決済み] Reactコンポーネントでthis.setStateを複数回使用するとどうなりますか?
-
[解決済み] TypeScriptプロジェクトで既存のC#クラス定義を再利用する方法
-
[解決済み] jQueryで入力ファイルが空かどうかをチェックする方法
-
[解決済み] JavaScriptでjson-objectのキーを取得する [重複].
-
[解決済み] JavaScriptやjQueryで値が数字かどうかをチェックする [重複]。
-
[解決済み] selectタグのngModelの変更を検出する方法(Angular 2)?
-
[解決済み] DataURLからBlob?
-
[解決済み] truthy や falsy を明示的なブール値、すなわち True や False に変換する。