node.js에서 한 번에 한 줄씩 파일을 읽습니까?


552

한 번에 한 줄씩 큰 파일을 읽으려고합니다. Quora 에서 주제를 다루는 질문을 찾았 지만 모든 것을 함께 사용할 수있는 연결이 누락되었습니다.

 var Lazy=require("lazy");
 new Lazy(process.stdin)
     .lines
     .forEach(
          function(line) { 
              console.log(line.toString()); 
          }
 );
 process.stdin.resume();

내가 알아 내고 싶은 것은이 샘플에서와 같이 STDIN 대신 파일에서 한 번에 한 줄씩 읽는 방법입니다.

나는 시도했다 :

 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의 실제 격차 인 것 같습니다.
hippietrail

5
이 방법으로 읽은 빈 줄은 단일 0 (실제 문자 코드 0)이있는 줄로 변환됩니다. 나는이 라인을 해킹해야했다 :if (line.length==1 && line[0] == 48) special(line);
Thabo

2
작업을 완벽하게 수행하는 'line-by-line'패키지를 사용할 수도 있습니다.
파트리스


2
@DanDascalescu 원하는 경우 목록에 추가 할 수 있습니다 : 귀하의 예제는 nodeAPI 문서 에서 약간 수정되었습니다. github.com/nodejs/node/pull/4609
eljefedelrodeodeljefe

답변:


788

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

final이 없어도 마지막 행은 올바르게 읽습니다 (Node v0.12 이상) \n.

업데이트 :이 예제는 노드의 API 공식 문서 추가 .


7
createInterface 정의에서 터미널 : 거짓이
필요함

64
마지막 줄을 결정하는 방법? "닫기"이벤트를 rl.on('close', cb)
Green

27
작성한 Readline은 유사한 목적을위한 GNU Readline와 , 하지 라인으로 파일 라인을 읽기 위해. 파일을 읽는 데 사용할 때 몇 가지주의 사항이 있으며 이는 모범 사례가 아닙니다.
Nakedible

8
@Nakedible : 흥미 롭습니다. 더 나은 방법으로 답변을 게시 할 수 있습니까?
Dan Dascalescu

6
github.com/jahewson/node-byline 을 줄 단위 읽기의 가장 좋은 구현으로 생각 하지만 의견은 다를 수 있습니다.
알몸

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. 즉, 모든 대안이 누락되었습니다 ( unicode.org/reports/tr18/#Line_Boundaries 참조 ). # 2, 마지막 이후의 데이터 \n는 자동으로 무시됩니다 ( stackoverflow.com/questions/18450197/… 참조 ). 이 솔루션을 위험한 원인 이라고 부릅니다. 모든 파일의 99 %와 데이터의 99 %에 대해서는 작동 하지만 나머지는 자동 으로 실패합니다 . 당신이 fs.writeFileSync( path, lines.join('\n'))할 때마다 위의 솔루션에 의해 부분적으로 읽힐 파일을 작성했습니다.
흐름

4
이 솔루션에 문제가 있습니다. your.js <lines.txt를 사용하면 마지막 줄이 표시되지 않습니다. 코스 끝에 '\ n'이 없으면
zag2art

readline패키지는 숙련 된 유닉스 / 리눅스 프로그래머에게 기괴한 방식으로 작동합니다.
Pointy

11
rd.on("close", ..);(전체 라인이 판독되는 경우가 발생할 수있는) 콜백로서 사용될 수있다
루카 Steeb에게

6
"마지막 \ n 이후 데이터"문제는 내 노드 버전 (0.12.7)에서 해결 된 것으로 보입니다. 그래서 나는 가장 간단하고 우아하게 보이는이 대답을 선호합니다.
Myk Melez

63

open파일 이 필요하지 않지만 대신을 만들어야합니다 ReadStream.

fs.createReadStream

그런 다음 해당 스트림을 Lazy


2
Lazy의 종료 이벤트와 같은 것이 있습니까? 모든 줄을 읽었습니까?
Max

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

6
@Cecchi와 @Max는 전체 파일을 메모리에 버퍼링하기 때문에 join을 사용하지 않습니다. 대신 '종료'이벤트를 듣습니다.new lazy(...).lines.forEach(...).on('end', function() {...})
Corin

3
@Cecchi, @Corin 및 @Max : 그만한 가치가 있기 때문에 이벤트를 처음 시작할 때 모든 것이 예상대로 작동했을 때 미친 체인 .on('end'... 만들었습니다 . .forEach(...)
crowjonah

52
이 결과는 검색 결과에서 매우 높으므로 Lazy가 버린 것처럼 보입니다. 7 개월 동안 아무런 변화가 없었으며 끔찍한 버그가 있습니다 (마지막 줄 무시, 대규모 메모리 누수 등).
blu

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-style"인터페이스를 사용하여 파일을 반복 할 수도 있습니다.

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.) 적어도 가능하다면 코드를 읽고 시도하는 것이 확실하지 않습니다.
Pointy

2
그 동안 readline코어 모듈을 사용하여 파일에서 행을 읽는 기본 제공 방법이 있습니다.
Dan Dascalescu

이것은 오래된이지만, 경우에 사람이 그것을 바탕으로 실수를 한단다 : function(reader)function(line)같아야 function(err,reader)하고 function(err,line).
jallmer

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

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

12
이 답변은 이전 답변정확히 일치 하지만 주석 경고 없으면 readline 패키지가 불안정한 것으로 표시 되며 (2015 년 4 월 현재 불안정) 2013 년 중반에 줄 끝없이 파일의 마지막 줄을 읽는 데 어려움이있었습니다 . v0.10.35에서 처음 사용했을 때 마지막 줄 문제가 발생하여 사라졌습니다. /
argh

파일 스트림에서 모든 작업을 읽은 경우 출력을 지정할 필요가 없습니다 .
Dan Dascalescu

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내가 누락되지 않은 한 ...을 제외 하고 임의의 파일로 작업하도록 확장 할 수있는 사소한 방법은 없습니다.
hippietrail

3
@hippietrail 당신은 ReadStreamwith with fs.createReadStream('./myBigFile.csv')을 (를) 대신 사용할 수 있습니다stdin
nolith

2
각 청크에는 완전한 행만 포함되어 있습니까? 멀티 바이트 UTF-8 문자가 청크 경계에서 분리되지 않습니까?
hippietrail

1
@hippietrail 멀티 바이트 문자 가이 구현에 의해 올바르게 처리된다고 생각하지 않습니다. 이를 위해서는 먼저 버퍼를 문자열로 올바르게 변환하고 두 버퍼간에 분리 된 문자를 추적해야합니다. 이를 위해 내장 된 StringDecoder를
Ernelli

그 동안 readline코어 모듈을 사용하여 파일에서 행을 읽는 기본 제공 방법이 있습니다.
Dan Dascalescu

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) { ...
Brent Faust

캐리어는 핸들에 보인다 \r\n\n라인 엔딩. OS X 이전의 MacOS 스타일 테스트 파일을 처리해야하는 경우 해당 파일을 사용 \r했으며 반송파는이를 처리하지 않습니다. 놀랍게도, 여전히 그러한 파일들이 야생에 떠 있습니다. 유니 코드 BOM (바이트 순서 표시)을 명시 적으로 처리해야 할 수도 있습니다. 이는 MS Windows 영향 영역에서 텍스트 파일의 시작 부분에 사용됩니다.
hippietrail

그 동안 readline코어 모듈을 사용하여 파일에서 행을 읽는 기본 제공 방법이 있습니다.
Dan Dascalescu

9

노드를 작동시키는 드레인 / 일시 정지 / 재개 방법으로 인해 해당 라인을 처리하고 다른 스트림에 쓰려고 할 때 Lazy를 사용하여 방대한 양의 메모리 누수가 발생했습니다 ( 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

Lazy 라인으로 이전 버전을 실행하면 누출이 발생합니다. 최신 버전은 전혀 누출되지 않으며 독자 / 프로세서의 기초로 사용할 수 있습니다. 나는 거기에 약간의 맞춤 물건이 있지만.

편집 : 필자는 필연적으로 배수 / 일시 중지 / 다시 시작하는 충분히 큰 XML 조각을 작성하는 것을 알 때까지 Lazy를 사용한 코드가 제대로 작동했음을 알아야합니다. 작은 덩어리에 대해서는 괜찮 았습니다.


그 동안 readline코어 모듈을 사용하여 파일에서 행을 읽는 훨씬 간단한 방법이 있습니다.
Dan Dascalescu

예. 이것이 올바른 방법입니다. 그러나 이것은 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

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으로로드하지 않음)
  • 선택적으로 배열의 모든 행을 반환합니다 (상세 또는 원시 모드)
  • 대화식으로 스트리밍 중단 또는 처리와 같은 맵 / 필터 수행
  • 개행 규칙 감지 (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)
hippietrail

2
@hippietrail : 문자 스트림이 아닌 바이트 스트림에서 작업하더라도 UTF-8에는 대답이 없습니다. 개행 (0x0a)에서 끊어집니다. UTF-8에서 멀티 바이트 문자의 모든 바이트에는 상위 비트가 설정됩니다. 따라서 멀티 바이트 문자에는 포함 된 줄 바꿈 또는 기타 일반적인 ASCII 문자를 포함 할 수 없습니다. 그러나 UTF-16과 UTF-32는 또 다른 문제입니다.
George

@ 조지 : 우리는 서로를 오해하는 것 같아요. CR과 LF는 모두 ASCII 범위 내에 있고 UTF-8은 128 개의 ASCII 문자를 그대로 유지하므로 CR이나 LF는 멀티 바이트 UTF-8 문자의 일부가 될 수 없습니다. 내가 요구 한 것은 여부 data에 대한 호출에 stream.on("data")같은 힘 이제까지 시작하거나 멀티 바이트 UTF-8 문자의 부분 종료 되는 U+10D0세 개의 바이트로 구성e1 83 90
hippietrail

1
이렇게하면 여전히 "새 줄"이되기 전에 전체 파일 내용을 메모리에로드합니다. 한 번에 한 줄씩 읽지 않고 대신 모든 줄을 가져 와서 "새 줄"버퍼 길이에 따라 구분합니다. 이 메소드는 스트림 작성의 목적을 무효화합니다.
Justin

그 동안 readline코어 모듈을 사용하여 파일에서 행을 읽는 훨씬 간단한 방법이 있습니다.
Dan Dascalescu

5

나는 기본적으로 Perl에서와 같은 문제를 해결하고 싶었다.

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

내 유스 케이스는 서버가 아닌 독립형 스크립트 였기 때문에 동기가 좋았습니다. 이것들은 나의 기준이었다 :

  • 많은 프로젝트에서 재사용 할 수있는 최소 동기 코드입니다.
  • 파일 크기 또는 줄 수에 제한이 없습니다.
  • 줄 길이에는 제한이 없습니다.
  • BMP 이외의 문자를 포함하여 UTF-8로 전체 유니 코드를 처리 할 수 ​​있습니다.
  • * 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

파일을 한 줄씩 읽고 다른 파일로 작성하려면 다음을 수행하십시오.

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

당신과 kofrasa의 대답의 차이점은 무엇입니까?
버팔로

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

1

또 다른 해결책은 순차적 실행기 nsynjs 를 통해 로직을 실행하는 입니다. 노드 readline 모듈을 사용하여 한 줄씩 파일을 읽으며 약속 또는 재귀를 사용하지 않으므로 큰 파일에서 실패하지 않습니다. 코드는 다음과 같습니다.

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

그러한 작업을 수행하는 동안 스스로에게 두 가지 질문을해야합니다.

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

큰 파일에서 특정 줄을 읽는 가장 좋은 방법은 node의 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);
  });

이 방법은 최대 4GB 밀도 파일로 성공적으로 테스트되었습니다.

big.text는 163,845 줄의 짙은 텍스트 파일이며 124MB입니다. 이 파일에서 10 개의 다른 행을 읽는 스크립트는 대략 4.63MB 메모리 만 사용합니다. 그리고 유효한 JSON을 객체 또는 배열로 무료로 구문 분석합니다. !! 최고 !!

메모리 사용량이 거의없는 한 줄의 파일이나 수백 줄의 파일을 읽을 수 있습니다.


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.