大きなファイルを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以降では、stable readline coreモジュールがあります。外部モジュールなしでファイルから行を読み込む最も簡単な方法は次のとおりです。
var lineReader = require('readline').createInterface({
input: require('fs').createReadStream('file.in')
});
lineReader.on('line', function (line) {
console.log('Line from file:', line);
});
最後の行は、最後の\n
がなくても(Node v0.12以降で)正しく読み込まれます。
_ update _ :この例は NodeのAPI公式ドキュメントに追加されました 。
このような単純な操作では、サードパーティ製のモジュールに依存してはいけません。簡単に行きます。
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);
});
行ごとにファイルを読むためのとてもいいモジュールがあります、それは line-reader と呼ばれます
それだけであなたは単に書く:
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);
});
}
});
require('fs').readFileSync('file.txt', 'utf-8').split(/\r?\n/).forEach(function(line){
console.log(line);
})
古い話題ですが、これはうまくいきます:
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
})
簡単です。外部モジュールは不要です。
あなたはいつでもあなた自身のラインリーダーを転がすことができます。このスニペットのベンチマークはまだ行っていませんが、チャンクの入ってくるストリームを末尾の '\ 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ソリューションは興味深いかもしれません。
var carrier = require('carrier');
process.stdin.resume();
carrier.carry(process.stdin, function(line) {
console.log('got one line: ' + line);
});
編集する
変換ストリーム を使用してください。
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 ();
結局、ノード内のdrain/pause/resumeが動作する方法が原因で、それらの行を処理してそれらを別のストリームに書き込もうとすると、Lazyを使用して大量のメモリリークが発生しました(参照: http:/ /elegantcode.com/2011/04/06/taking-baby-steps-with-node-js-pumping-data-between-streams/ /(私はところでこの人が大好きです))。その理由を正確に理解するためにLazyを十分に詳しく調べていませんでしたが、Lazyを終了せずにドレインを可能にするために読み取りストリームを一時停止することはできませんでした。
私はxmlドキュメントに巨大なcsvファイルを処理するためのコードを書きました、あなたはここでコードを見ることができます: https://github.com/j03m/node-csv2xml
あなたがLazy lineを使って以前のリビジョンを実行すると、それはリークします。最新のリビジョンはまったくリークしていないので、おそらくそれをリーダー/プロセッサの基盤として使用することができます。私はそこにいくつかのカスタムものを持っていますが。
編集:私は自分が必要であるために排水/一時停止/再開する十分な大きさのxmlフラグメントを書いていることに気づくまで、私のLazyコードはうまくいったことにも注意すべきだと思います。小さい塊のためにそれは大丈夫だった。
私はこれに対する包括的な解決策がないことに不満を感じたので、私は自分自身の試みをまとめました( git / npm )。コピーペーストされた機能のリスト:
NIH?あなたが決める :-)
私の最初の答えを投稿して以来、私は split がファイルの行を読むための非常に使いやすいノードモジュールであることがわかりました。これはオプションのパラメータも受け付けます。
var split = require('split');
fs.createReadStream(file)
.pipe(split())
.on('data', function (line) {
//each chunk now is a seperate 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)
});
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("++++++++++++++++++++")
})
私はこれと同じ問題に取り組みたいと思っていました。基本的にPerlのものは次のようになります。
while (<>) {
process_line($_);
}
私のユースケースは、サーバーではなく、単体のスクリプトだけだったので、同期は問題ありませんでした。これらが私の基準でした:
これは私がnode.jsの低レベルのスクリプティングタイプのコードを理解し、それがPerlのような他のスクリプティング言語に代わるものとしてどれほど実行可能かを判断するためのプロジェクトです。
驚くべき努力と2、3の誤った開始の後、これは私が思い付いたコードです。それはかなり速いですが私が予想していたよりも自明ではありません: (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;
}
}
}
おそらくさらにクリーンアップすることができます、それは試行錯誤の結果でした。
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であり、大きなファイルを非常にすばやく読み取ることができました
これが役立つことを願っています
発電機ベースのラインリーダー: 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());
}
});
});
ファイルを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');
});
};
別の解決策は、シーケンシャルエグゼキュータ nsynjs を介してロジックを実行することです。ノードreadlineモジュールを使用してファイルを行単位で読み取ります。また、promiseやrecursionを使用しないため、大きなファイルで失敗することはありません。コードは次のようになります。
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
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();
これをうまく行う小さなモジュールがあり、他の多くのプロジェクトで使用されています npm readline ノードv10にはネイティブreadlineモジュールがあるので注意してくださいモジュールをlinebylineとして再公開しました 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);
私は毎日のライン処理のロジック全体をnpmモジュールとしてラップしています: line-kit https://www.npmjs.com/package/line-kit
// example
var count = 0
require('line-kit')(require('fs').createReadStream('/etc/issue'),
(line) => { count++; },
() => {console.log(`seen ${count} lines`)})
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 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-
上の答えが示すように、おそらく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);
});
}
}