Читайте файл по одной строке в node.js?


553

Я пытаюсь прочитать большой файл по одной строке за раз. Я нашел вопрос о 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(). Вы можете читать двоичные октеты в буфер, но нет простого способа справиться с частичными символами UTF-8 или UTF-16 без проверки буфера перед его преобразованием в строки JavaScript и сканированием на наличие EOL. Buffer()Тип не имеет как богатый набор функций для работы на его экземплярах , как родные строки, но родные строки не могут содержать двоичные данные. Мне кажется, что отсутствие встроенного способа чтения текстовых строк из произвольных файловых дескрипторов является настоящим пробелом в node.js.
hippietrail

5
Пустые строки, считанные этим методом, преобразуются в строку с одним 0 (фактический код символа для 0) в них. Я должен был взломать эту строку там:if (line.length==1 && line[0] == 48) special(line);
Thabo

2
Можно также использовать пакетный, который отлично справляется со своей задачей.
Патрис

1
Пожалуйста, обновите вопрос, чтобы сказать, что решение состоит в том, чтобы использовать поток преобразования
Габриэль Ламас

2
@DanDascalescu, если хотите, можете добавить это в список: ваш пример немного изменен в nodeдокументации по API github.com/nodejs/node/pull/4609
eljefedelrodeodeljefe

Ответы:


790

Начиная с 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);
});

Последняя строка читается правильно (начиная с Node v0.12 или более поздней), даже если финальной версии нет \n.

ОБНОВЛЕНИЕ : этот пример был добавлен к официальной документации Node API .


7
вам нужен терминал: false в определении
createInterface

64
Как определить последнюю строку? Улавливая «близкое» событие:rl.on('close', cb)
Green

27
Readline для аналогичной цели GNU Readline , а не для чтения файлов построчно. Есть несколько предостережений при использовании его для чтения файлов, и это не лучшая практика.
Nakedible

8
@Nakedible: интересно. Не могли бы вы опубликовать ответ с лучшим методом?
Дан Даскалеску

6
Я считаю github.com/jahewson/node-byline лучшей реализацией построчного чтения, но мнения могут отличаться.
Nakedible

165

Для такой простой операции не должно быть никакой зависимости от сторонних модулей. Полегче.

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пакет ведет себя по- настоящему странным образом к опытному Unix / Linux программиста.
Заостренный

11
rd.on("close", ..);может использоваться как обратный вызов (происходит, когда все строки прочитаны)
Лука Стиб

6
Проблема «данные после последней \ n», кажется, решена в моей версии узла (0.12.7). Поэтому я предпочитаю этот ответ, который кажется самым простым и элегантным.
Myk Melez

63

Вам не нужно openфайл, но вместо этого вы должны создать ReadStream.

fs.createReadStream

Затем передайте этот поток Lazy


2
Есть ли что-то вроде окончания мероприятия для Lazy? Когда все строки были прочитаны?
Макс

1
@Max, попробуйте:new lazy(fs.createReadStream('...')).lines.forEach(function(l) { /* ... */ }).join(function() { /* Done */ })
Cecchi

6
@Cecchi и @Max, не используйте объединение, потому что оно буферизует весь файл в памяти. Вместо этого просто послушайте событие «конец»:new lazy(...).lines.forEach(...).on('end', function() {...})
Корин

3
@Cecchi, @Corin и @Max: Для чего бы это ни стоило, я сводил себя с ума, цепляясь .on('end'... после того .forEach(...) , как на самом деле все выглядело так, как ожидалось, когда я связал событие первым .
Crowjonah

52
Этот результат очень высок в результатах поиска, поэтому стоит отметить, что Lazy выглядит заброшенным. Прошло 7 месяцев без каких-либо изменений, и в нем есть ужасающие ошибки (последняя строка игнорируется, серьезные утечки памяти и т. Д.).
Blu

38

есть очень хороший модуль для чтения файла построчно, он называется 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);
    });
  }
});

4
Это хорошо работает. Он даже читает последнюю строку (!). Стоит отметить, что он сохраняет \ r, если это текстовый файл в стиле Windows. line.trim () помогает избавиться от лишних \ r.
Пьер-Люк Бертран

Это неоптимально в том, что входные данные могут быть только из именованного файла, а не (для очевидного и чрезвычайно важного примера process/stdin). По крайней мере, если это возможно, это, конечно, не очевидно из чтения кода и его попытки.
Заостренный

2
В то же время есть встроенный способ чтения строк из файла, используя readlineмодуль ядра .
Дан Даскалеску

Это старо, но в случае, если кто-то наткнется на это: function(reader)и function(line)должно быть: function(err,reader)и function(err,line).
jallmer

1
Просто для записи, line-readerчитает файл асинхронно. Синхронная альтернатива этомуline-reader-sync
Праджвал Дхатвалиа

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

42
Это прочитает весь файл в памяти, а затем разделит его на строки. Это не то, что задают вопросы. Дело в том, чтобы иметь возможность читать большие файлы последовательно, по требованию.
Дан Даскалеску

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

Спасибо, это мило.
Горан Стоянов

3
Может быть, это очевидно для других, но мне потребовалось некоторое время для отладки: если у вас есть какие-либо awaits между createInterface()вызовом и началом for awaitцикла, вы таинственным образом потеряете строки с начала файла. createInterface()немедленно начинает испускать строки за кулисами, и асинхронный итератор, неявно созданный с, 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 года), и в середине 2013 года возникли проблемы с чтением последних строк файла без окончаний строк . Последняя проблема с строкой возникла в первый раз, когда я использовал ее в v0.10.35, и затем ушла. /
Argh

Вам не нужно указывать вывод, если все, что вы делаете, это считываете из файлового потока .
Дан Дакалеску

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

Я придумал это, работая над сценарием быстрого анализа журнала, который должен был накапливать данные во время анализа журнала, и я чувствовал, что было бы неплохо попробовать сделать это, используя js и node вместо использования perl или bash.

В любом случае, я чувствую, что небольшие сценарии nodejs должны быть автономными и не полагаться на сторонние модули, поэтому после прочтения всех ответов на этот вопрос, каждый из которых использует различные модули для обработки разборов строк, может оказаться интересным решение 13 SLOC для нативных nodejs.


Кажется, что нет никакого тривиального способа расширить это для работы с произвольными файлами, кроме просто stdin... если я не пропустил что-нибудь.
hippietrail

3
@hippietrail вы можете создать ReadStreamс fs.createReadStream('./myBigFile.csv')и использовать его вместоstdin
Нолит

2
Каждый блок гарантированно содержит только полные строки? Гарантируется, что многобайтовые символы UTF-8 не будут разделены на границах фрагментов?
hippietrail

1
@hippietrail Я не думаю, что эта реализация правильно обрабатывает многобайтовые символы. Для этого нужно сначала правильно преобразовать буферы в строки и отслеживать символы, которые разделены между двумя буферами. Чтобы сделать это правильно, можно использовать встроенный StringDecoder
Ernelli

В то же время есть встроенный способ чтения строк из файла, используя readlineмодуль ядра .
Дан Даскалеску

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символы конца строки. Если вам когда-нибудь потребуется иметь дело с тестовыми файлами в стиле MacOS, выпущенными до OS X, они используются, \rи оператор не справится с этим. Удивительно, но в дикой природе еще существуют такие файлы. Возможно, вам также придется обрабатывать спецификацию Unicode (метку порядка байтов), это используется в начале текстовых файлов в сфере влияния MS Windows.
hippietrail

В то же время есть встроенный способ чтения строк из файла, используя readlineмодуль ядра .
Дан Даскалеску

9

Я закончил с огромной утечкой памяти, используя Lazy для чтения построчно при попытке затем обработать эти строки и записать их в другой поток из-за того, как работает сток / пауза / возобновление в узле (см .: http: // Elegantcode .com / 2011/04/06 / брать ребенка шаги с узлом js накачка данных между потоками / (я люблю этого парня, кстати)). Я не достаточно внимательно посмотрел на Lazy, чтобы понять, почему именно, но я не мог приостановить поток чтения, чтобы обеспечить сток без выхода Lazy.

Я написал код для обработки больших CSV-файлов в XML-документы, вы можете увидеть код здесь: https://github.com/j03m/node-csv2xml

Если вы запускаете предыдущие ревизии с ленивой линией, она просачивается. Последняя версия вообще не имеет утечки, и вы, вероятно, можете использовать ее как основу для считывателя / процессора. Хотя у меня есть кое-какие нестандартные вещи.

Изменить: Я думаю, я должен также отметить, что мой код с Lazy работал нормально, пока я не обнаружил, что пишу достаточно большие фрагменты XML, которые истощают / пауза / резюме, потому что это необходимо. Для небольших кусков это было хорошо.


В то же время есть гораздо более простой способ чтения строк из файла, используя readlineосновной модуль .
Дан Даскалеску

Ага. Это правильный путь сейчас. Но это было с 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основной модуль .
Дан Даскалеску

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 ). Вставленный в копию список функций:

  • Интерактивная обработка строк (на основе обратного вызова, без загрузки всего файла в оперативную память)
  • Опционально, вернуть все строки в массиве (подробный или необработанный режим)
  • Интерактивно прерывать потоковую передачу или выполнять обработку, подобную карте / фильтру
  • Обнаружение любого соглашения новой строки (ПК / Mac / Linux)
  • Правильное лечение eof / last line
  • Правильная обработка многобайтовых символов UTF-8
  • Получать информацию о смещении байта и длине байта для каждой строки
  • Произвольный доступ с использованием линейных или байтовых смещений
  • Автоматически отображать информацию о смещении линии, чтобы ускорить произвольный доступ
  • Нулевые зависимости
  • тесты

НАЦИОНАЛЬНЫЕ ИНСТИТУТЫ ЗДРАВООХРАНЕНИЯ США? Вам решать :-)


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 - другое дело, однако.
Джордж

@ Джордж: Я думаю, что мы не понимаем друг друга. Поскольку CR и LF находятся в пределах диапазона ASCII, а UTF-8 сохраняет 128 символов ASCII без изменений, ни CR, ни LF никогда не могут быть частью многобайтового символа UTF-8. То , что я спрашиваю , является ли dataв вызове stream.on("data")могуществе когда начинаются или заканчиваются только часть многобайтовыми UTF-8 символов , таких , как , который U+10D0, состоит из трех байтовe1 83 90
hippietrail

1
Это все еще загружает все содержимое файла в память, прежде чем сделать его «новой строкой». Это не ЧИТАЕТ по одной строке за раз, вместо этого он берет ВСЕ строки и затем разбивает их в соответствии с длиной буфера «новой строки». Этот метод побеждает цель создания потока.
Джастин

В то же время есть гораздо более простой способ чтения строк из файла, используя readlineосновной модуль .
Дан Даскалеску

5

Я хотел решить эту же проблему, в основном то, что было бы в Perl:

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

Мой вариант использования был всего лишь автономным сценарием, а не сервером, поэтому синхронный был в порядке. Это были мои критерии:

  • Минимальный синхронный код, который можно использовать во многих проектах.
  • Нет ограничений на размер файла или количество строк.
  • Нет ограничений по длине линий.
  • Способен обрабатывать полный Unicode в UTF-8, включая символы за пределами BMP.
  • Умеет обрабатывать * 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');
   });
};

В чем разница между твоим и Кофрасой ответом?
Буффало

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/ построчно

если вы не хотите использовать модуль, функция очень проста:

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

Прямо сейчас лучший способ прочитать определенные строки из большого файла - использовать 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);
  });

Этот метод был успешно протестирован с плотными файлами до 4 ГБ.

big.text представляет собой плотный текстовый файл с 163 845 строками и объемом 124 Мб. Скрипт для чтения 10 различных строк из этого файла использует только 4,63 МБ памяти. И он анализирует действительный 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')
}))
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.