Читати файл по одному рядку в 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(). Ви можете читати двійкові октети в буфер, але немає простого способу впоратися з частковими символами 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, якщо вам подобається, ви можете додати це до списку: ваш приклад дещо змінений у nodeAPI API docs github.com/nodejs/node/pull/4609
eljefedelrodeodeljefe

Відповіді:


788

Оскільки Node.js v0.12 і станом на Node.js v4.0.0, існує стабільний основний модуль для читання . Ось найпростіший спосіб читати рядки з файлу без зовнішніх модулів:

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.

ОНОВЛЕННЯ : цей приклад додано до офіційної документації API Node .


7
вам потрібен термінал: false у визначенні
createInterface

64
Як визначити останній рядок? Спіймаючи "близьку" подію:rl.on('close', cb)
Зелений

27
Readline призначена для подібної мети, як GNU Readline , а не для читання файлів по черзі. Існує кілька застережень щодо його використання для читання файлів, і це не найкраща практика.
Голий

8
@Nakedible: цікаво. Чи можете ви опублікувати відповідь кращим методом?
Дан Даскалеску

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пакет поводиться по- справжньому дивним чином до досвідченого Unix / Linux програміста.
Pointy

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

6
Здається, "дані після останнього \ n" проблеми вирішені у моїй версії вузла (0.12.7). Тому я віддаю перевагу цій відповіді, яка здається найпростішою та найелегантнішою.
Myk Melez

63

Вам не потрібно до openфайлу, але замість цього потрібно створити ReadStream.

fs.createReadStream

Потім передайте цей потік Lazy


2
Чи є щось подібне до кінцевої події для Ледачих? Коли всі рядки прочитані?
Макс

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(...) , коли насправді все поводилося так, як очікувалося, коли я спочатку зв'язав цю подію .
кройона

52
Цей результат дуже високий в результатах пошуку, тому варто відзначити, що Ледачий виглядає покинутим. Минуло 7 місяців без будь-яких змін і має деякі жахливі помилки (останній рядок ігнорується, масові витоки пам'яті тощо).
блю

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, якщо вам потрібно більше контролю:

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

4
Це добре працює. Він навіть читає останній рядок (!). Варто згадати, що він зберігає \, якщо це текстовий файл у стилі Windows. line.trim () виконує трюк для видалення зайвих \ r.
П’єр-Люк Бертран

Це неоптимально, тому що введення може бути тільки з названого файлу, а не (для очевидного і надзвичайно важливого прикладу process/stdin). Принаймні, якщо це можливо, це точно не очевидно з читання коду та спроб його.
Pointy

2
Тим часом існує вбудований спосіб зчитування рядків з файлу за допомогою readlineосновного модуля .
Дан Даскалеску

Це старе, але якщо хтось натикається на нього: function(reader)і function(line)має бути: function(err,reader)і function(err,line).
тушканчик

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
Це прочитає весь файл у пам'яті, а потім розділить його на рядки. Це не те, що задає питання. Сенс у тому, щоб мати можливість читати великі файли послідовно, на вимогу.
Дан Даскалеску

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
Ця відповідь є точним результатом попередньої відповіді , але без зауважень, які попереджають коментарі, пакет для читання рядків позначається нестабільним (досі нестабільним станом на квітень 2015 року), і в середині 2013 року виникли проблеми з читанням останніх рядків файлу без закінчень рядків . Останній випуск рядка вирізався в перший раз, коли я використовував його в v0.10.35, а потім пішов. / argh
ruffin

Не потрібно вказувати вихід, якщо все, що ви робите, читається з потоку файлів .
Дан Даскалеску

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-рішень може бути цікавим.


Здається, не існує жодного тривіального способу розширити це на роботу з довільними файлами, крім просто stdin... якщо я не пропущу щось.
hippietrail

3
@hippietrail ви можете створити ReadStreamс fs.createReadStream('./myBigFile.csv')і використовувати його замістьstdin
nolith

2
Чи гарантовано кожен фрагмент містить лише цілі рядки? Чи гарантовано багатобайтові символи UTF-8, які не розбиваються на межі?
hippietrail

1
@hippietrail Я не думаю, що цією реалізацією керуються багатобайтові символи. Для цього спочатку потрібно правильно перетворити буфери в рядки та відслідковувати символи, розділені між двома буферами. Щоб правильно це зробити, можна використовувати вбудований StringDecoder
Ернеллі

Тим часом існує вбудований спосіб зчитування рядків з файлу за допомогою 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 раніше, ніж у ОС X, вони використовувались, \rі оператор не справляється з цим. Дивно, але все ще такі файли пливуть у дикій природі. Можливо, вам також потрібно буде чітко обробляти Unicode BOM (байт порядку), це використовується на початку текстових файлів у сфері впливу MS Windows.
hippietrail

Тим часом існує вбудований спосіб зчитування рядків з файлу за допомогою readlineосновного модуля .
Дан Даскалеску

9

Я закінчився великим, масовим витоком пам’яті, використовуючи Lazy для читання рядка за рядком, коли намагався обробити ці рядки та записати їх в інший потік завдяки способу «дренаж / пауза / відновлення» у вузлі (див .: http: // elegantcode .com / 2011/04/06 / кроки-дитина-кроки-з-вузлом-js-перекачування даних-між-потоками / (мені дуже подобається цей хлопець btw)). Я не придивився уважно до Лази, щоб зрозуміти, чому саме це було, але я не міг призупинити свій потік читання, щоб дозволити стік без Лейзи виходу.

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

Якщо ви запустите попередні версії за допомогою рядка Lazy, вона просочиться. Остання редакція зовсім не протікає, і ви, ймовірно, можете використовувати її як основу для читання / процесора. Хоча в мене є якісь спеціальні речі.

Редагувати: Напевно, я також повинен зауважити, що мій код із 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

Опублікувавши оригінальну відповідь, я виявив, що спліт є дуже простим у використанні модулем вузла для читання рядків у файлі; Що також приймає необов'язкові параметри.

var split = require('split');
fs.createReadStream(file)
    .pipe(split())
    .on('data', function (line) {
      //each chunk now is a seperate line! 
    });

Не перевірявся на дуже великих файлах. Повідомте нас, якщо ви зробите це.


6

Мене розчарувало відсутність комплексного рішення для цього, тому я зібрав власну спробу ( git / npm ). Скопіюйте вкладений список функцій:

  • Інтерактивна обробка рядків (на основі зворотного дзвінка, не завантажуючи весь файл в оперативну пам’ять)
  • Необов’язково повернути всі рядки в масив (детальний або необроблений режим)
  • Інтерактивно переривати потокове або виконувати обробку карти / фільтра, як
  • Виявити будь-яку конвенцію про новий рядок (PC / 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)
гіппетрейл

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
Це все ще завантажує весь вміст файлу в пам'ять, перш ніж зробити його "новим рядком". Це не читає один рядок за часом, він замість цього приймає ВСІ рядки, а потім розбиває їх відповідно до довжини буфера "новий рядок". Цей метод перемагає мету створення потоку.
Джастін

Тим часом існує набагато простіший спосіб зчитувати рядки з файлу, використовуючи 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 є власний модуль для читання, тому я перевидав свій модуль як 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 . Він читає файл по черзі за допомогою модуля зчитування вузлів, і він не використовує обіцянки чи рекурсію, тому не збирається виходити з ладу для великих файлів. Ось як буде виглядати код:

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

скористайтеся цією функцією в потоці і прослухайте події, які буде випромінюватися.

гр-


0

Хоча ви, ймовірно, використовуєте 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

Я переглянув усі вищезазначені відповіді, всі вони використовують сторонні бібліотеки для її вирішення. У API Node є просте рішення. напр

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.