Javascript / Node.js에서 큰 (5-10Gb) 로그 파일을 파싱해야합니다 (Cube를 사용하고 있습니다).
로그 라인은 다음과 같습니다.
10:00:43.343423 I'm a friendly log message. There are 5 cats, and 7 dogs. We are in state "SUCCESS".
우리는 (예 : 빼내야 몇 가지 분석을, 각 라인을 읽을 필요가 5
, 7
그리고 SUCCESS
다음 큐브로이 데이터 (펌프) https://github.com/square/cube를 자신의 JS 클라이언트를 사용하여).
첫째, 노드에서 파일을 한 줄씩 읽는 표준 방법은 무엇입니까?
온라인에서 매우 일반적인 질문 인 것 같습니다.
- http://www.quora.com/What-is-the-best-way-to-read-a-file-line-by-line-in-node-js
- node.js에서 한 번에 한 줄씩 파일을 읽습니까?
많은 답변이 여러 타사 모듈을 가리키는 것 같습니다.
- https://github.com/nickewing/line-reader
- https://github.com/jahewson/node-byline
- https://github.com/pkrumins/node-lazy
- https://github.com/Gagle/Node-BufferedReader
그러나 이것은 매우 기본적인 작업처럼 보입니다. 확실히 stdlib 내에 텍스트 파일을 한 줄씩 읽는 간단한 방법이 있습니까?
둘째, 그런 다음 각 줄을 처리해야합니다 (예 : 타임 스탬프를 Date 객체로 변환하고 유용한 필드 추출).
처리량을 최대화하는 가장 좋은 방법은 무엇입니까? 각 줄을 읽거나 큐브로 보내는 것을 차단하지 않는 방법이 있습니까?
셋째-문자열 분할을 사용하여 추측하고 있으며 JS에 해당하는 contains (IndexOf! = -1?)가 정규식보다 훨씬 빠를 것입니까? Node.js에서 방대한 양의 텍스트 데이터를 파싱 한 경험이있는 사람이 있습니까?
건배, 빅터
답변
스트림을 사용하여 한 줄씩 매우 큰 파일 (gbs)을 구문 분석하는 솔루션을 찾았습니다. 모든 타사 라이브러리와 예제는 파일을 한 줄씩 처리하지 않았거나 (예 : 1, 2, 3, 4 ..) 전체 파일을 메모리로 읽었 기 때문에 내 요구에 맞지 않았습니다.
다음 솔루션은 스트림 및 파이프를 사용하여 한 줄씩 매우 큰 파일을 구문 분석 할 수 있습니다. 테스트를 위해 17.000.000 레코드가있는 2.1GB 파일을 사용했습니다. RAM 사용량은 60MB를 초과하지 않았습니다.
먼저 이벤트 스트림 패키지를 설치합니다 .
npm install event-stream
그때:
var fs = require('fs')
, es = require('event-stream');
var lineNr = 0;
var s = fs.createReadStream('very-large-file.csv')
.pipe(es.split())
.pipe(es.mapSync(function(line){
// pause the readstream
s.pause();
lineNr += 1;
// process line here and call s.resume() when rdy
// function below was for logging memory usage
logMemoryUsage(lineNr);
// resume the readstream, possibly from a callback
s.resume();
})
.on('error', function(err){
console.log('Error while reading file.', err);
})
.on('end', function(){
console.log('Read entire file.')
})
);
어떻게되는지 알려주세요!
답변
내장 readline
패키지를 사용할 수 있습니다 . 여기에서 문서를 참조 하십시오 . 내가 사용하는 스트리밍 새로운 출력 스트림을 만들 수 있습니다.
var fs = require('fs'),
readline = require('readline'),
stream = require('stream');
var instream = fs.createReadStream('/path/to/file');
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) {
console.log(line);
//Do your stuff ...
//Then write to outstream
rl.write(cubestuff);
});
대용량 파일은 처리하는 데 다소 시간이 걸립니다. 작동하는지 말하십시오.
답변
나는 실제로 여기에서 정답이 될 자격이있는 @gerard 답변을 정말 좋아했습니다 . 몇 가지 개선했습니다.
- 코드가 클래스에 있음 (모듈 식)
- 구문 분석이 포함됩니다.
- DB에 삽입 또는 HTTP 요청과 같은 CSV 읽기에 연결된 비동기 작업이있는 경우 재개 할 수있는 기능이 외부에 부여됩니다.
- 사용자가 선언 할 수있는 청크 / 배치 크기로 읽기. 다른 인코딩의 파일이있는 경우를 대비하여 스트림에서 인코딩도 처리했습니다.
코드는 다음과 같습니다.
'use strict'
const fs = require('fs'),
util = require('util'),
stream = require('stream'),
es = require('event-stream'),
parse = require("csv-parse"),
iconv = require('iconv-lite');
class CSVReader {
constructor(filename, batchSize, columns) {
this.reader = fs.createReadStream(filename).pipe(iconv.decodeStream('utf8'))
this.batchSize = batchSize || 1000
this.lineNumber = 0
this.data = []
this.parseOptions = {delimiter: '\t', columns: true, escape: '/', relax: true}
}
read(callback) {
this.reader
.pipe(es.split())
.pipe(es.mapSync(line => {
++this.lineNumber
parse(line, this.parseOptions, (err, d) => {
this.data.push(d[0])
})
if (this.lineNumber % this.batchSize === 0) {
callback(this.data)
}
})
.on('error', function(){
console.log('Error while reading file.')
})
.on('end', function(){
console.log('Read entirefile.')
}))
}
continue () {
this.data = []
this.reader.resume()
}
}
module.exports = CSVReader
따라서 기본적으로 사용 방법은 다음과 같습니다.
let reader = CSVReader('path_to_file.csv')
reader.read(() => reader.continue())
나는 이것을 35GB CSV 파일로 테스트했으며 저에게 효과적이었습니다. 그래서 @gerard 의 답변 을 기반으로 작성하기로 결정했으며 피드백을 환영합니다.
답변
텍스트 파일에서 1000000 줄 이상을 읽기 위해 https://www.npmjs.com/package/line-by-line 을 사용했습니다 . 이 경우 RAM의 점유 용량은 약 50-60MB입니다.
const LineByLineReader = require('line-by-line'),
lr = new LineByLineReader('big_file.txt');
lr.on('error', function (err) {
// 'err' contains error object
});
lr.on('line', function (line) {
// pause emitting of lines...
lr.pause();
// ...do your asynchronous line processing..
setTimeout(function () {
// ...and continue emitting lines.
lr.resume();
}, 100);
});
lr.on('end', function () {
// All lines are read, file is closed now.
});
답변
큰 파일을 한 줄씩 읽는 것 외에도 청크 단위로 읽을 수도 있습니다. 자세한 내용은 이 기사를 참조 하십시오.
var offset = 0;
var chunkSize = 2048;
var chunkBuffer = new Buffer(chunkSize);
var fp = fs.openSync('filepath', 'r');
var bytesRead = 0;
while(bytesRead = fs.readSync(fp, chunkBuffer, 0, chunkSize, offset)) {
offset += bytesRead;
var str = chunkBuffer.slice(0, bytesRead).toString();
var arr = str.split('\n');
if(bytesRead = chunkSize) {
// the last item of the arr may be not a full line, leave it to the next chunk
offset -= arr.pop().length;
}
lines.push(arr);
}
console.log(lines);
답변
Node.js 문서는 Readline 모듈을 사용하는 매우 우아한 예제를 제공합니다.
const fs = require('fs');
const readline = require('readline');
const rl = readline.createInterface({
input: fs.createReadStream('sample.txt'),
crlfDelay: Infinity
});
rl.on('line', (line) => {
console.log(`Line from file: ${line}`);
});
참고 : crlfDelay 옵션을 사용하여 CR LF ( ‘\ r \ n’)의 모든 인스턴스를 단일 줄 바꿈으로 인식합니다.
답변
나는 아직 같은 문제가 있었다. 이 기능이있는 것처럼 보이는 여러 모듈을 비교 한 후 직접 해보기로 결정했는데 생각보다 간단했습니다.
요점 : https://gist.github.com/deemstone/8279565
var fetchBlock = lineByline(filepath, onEnd);
fetchBlock(function(lines, start){ ... }); //lines{array} start{int} lines[0] No.
그것은 클로저로 열린 파일을 덮고, fetchBlock()
반환 된 파일에서 블록을 가져오고 배열로 분할을 종료합니다 (마지막 가져 오기에서 세그먼트를 처리합니다).
각 읽기 작업에 대해 블록 크기를 1024로 설정했습니다. 버그가있을 수 있지만 코드 논리는 분명합니다. 직접 시도해보세요.