javascript - Läsa en fil en rad i taget i node.js?


Jag försöker läsa en stor fil en rad i taget. Jag hittade en fråga på Quora som behandlade ämnet men jag saknar vissa kopplingar för att få allt att passa ihop.

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

Det jag skulle vilja lista ut är hur jag kan läsa en rad i taget från en fil istället för från STDIN som i detta exempel.

Jag försökte:

 fs.open('./VeryBigFile.csv', 'r', '0666', Process);

 function Process(err, fd) {
    if (err) throw err;
    // DO lazy read 
 }

men det fungerar inte. Jag vet att jag i en nödsituation skulle kunna falla tillbaka på att använda något som PHP, men jag skulle vilja lista ut det här.

Jag tror inte att det andra svaret skulle fungera eftersom filen är mycket större än den minne servern jag kör den på har.

Svar #1

Sedan Node.js v0.12 och från och med Node.js v4.0.0 finns det en stabil readline-kärnmodul. Här är det enklaste sättet att läsa rader från en fil, utan några externa moduler:

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

Eller alternativt:

var lineReader = require('readline').createInterface({
  input: require('fs').createReadStream('file.in')
});

lineReader.on('line', function (line) {
  console.log('Line from file:', line);
});

lineReader.on('close', function () {
    console.log('all done, son');
});

Den sista raden läses korrekt (från och med Node v0.12 eller senare), även om det inte finns någon slutlig \n.

UPPDATERING: detta exempel har lagts till i Nodes officiella API-dokumentation.

Svar #2

För en så enkel operation borde det inte finnas något beroende av tredjepartsmoduler. Ta det lugnt.

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

rd.on('close', function() {
    console.log('all done, son');
});

Svar #3

Uppdatering 2019

Ett fantastiskt exempel finns redan publicerat i den officiella Nodejs-dokumentationen. här

Detta kräver att den senaste versionen av Nodejs är installerad på din dator. >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();

Svar #4

require('fs').readFileSync('file.txt', 'utf-8').split(/\r?\n/).forEach(function(line){
  console.log(line);
})

Svar #5

Du behöver inte öppna filen, utan du måste istället skapa en ReadStream.

fs.createReadStream

Sedan skicka den strömmen till Lazy

Svar #6

det finns en mycket trevlig modul för att läsa en fil rad för rad, den heter line-reader

med det skriver du helt enkelt bara:

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

du kan till och med iterera filen med ett "java-stil" gränssnitt, om du behöver mer kontroll:

lineReader.open('file.txt', function(reader) {
  if (reader.hasNextLine()) {
    reader.nextLine(function(line) {
      console.log(line);
    });
  }
});

Svar #7

En ny funktion lades till i Node.js v18.11.0 för att läsa filer rad för rad

  • filehandle.readLines([alternativ])

Så här använder du detta med en textfil som du vill läsa

import { open } from 'node:fs/promises';
myFileReader();
async function myFileReader() {
    const file = await open('./TextFileName.txt');
    for await (const line of file.readLines()) {
        console.log(line)
    }
}

För att förstå mer, läs Node.js-dokumentationen här är länken till file system readlines(): https://nodejs.org/api/fs.html#filehandlereadlinesoptions

Svar #8

Du kan alltid skapa din egen linjeläsare. Jag har inte benchmarkat denna kodsnutt än, men den delar korrekt upp den inkommande strömmen av bitar i rader utan den avslutande '\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();

Jag kom faktiskt på detta när jag arbetade på ett snabbt skript för loggparsing som behövde samla data under loggparsningen och jag tyckte att det skulle vara trevligt att försöka göra detta med js och node istället för att använda perl eller bash.

Hur som helst, jag känner att små nodejs-skript bör vara självständiga och inte förlita sig på tredjepartsmoduler, så efter att ha läst alla svar på denna fråga, där varje använde olika moduler för att hantera radanalys, kan en 13 SLOC native nodejs-lösning vara av intresse.

Svar #9

Gammalt ämne, men detta fungerar:

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

Enkelt. Ingen extern modul behövs.

Svar #10

Jag slutade med ett enormt, enormt minnesläckage när jag använde Lazy för att läsa rad för rad när jag försökte bearbeta dessa rader och skriva dem till en annan ström på grund av hur drain/pause/resume fungerar i Node (se: http://elegantcode.com/2011/04/06/taking-baby-steps-with-node-js-pumping-data-between-streams/ (jag älskar den här killen förresten)). Jag har inte tittat noggrant på Lazy för att förstå exakt varför, men jag kunde inte pausa min läsström för att tillåta en drain utan att Lazy avslutades.

Jag skrev koden för att bearbeta massiva csv-filer till xml-dokument, du kan se koden här: https://github.com/j03m/node-csv2xml

Om du kör de tidigare revideringarna med Lazy-linjen läcker det. Den senaste revideringen läcker inte alls och du kan troligen använda den som grund för en läsare/processorer. Även om jag har några anpassade saker där.

Redigera: Jag antar att jag också borde nämna att min kod med Lazy fungerade bra tills jag upptäckte att jag skrev tillräckligt stora XML-fragment som kräver att tömma/pausa/återuppta som en nödvändighet. För mindre bitar fungerade det bra.

Svar #11

Med bärarmodulen:

var carrier = require('carrier');

process.stdin.resume();
carrier.carry(process.stdin, function(line) {
    console.log('got one line: ' + line);
});

Relaterade artiklar