node.js: читать текстовый файл в массив. (Каждая строка элемента в массиве.)

Я хотел бы прочитать очень очень большой файл в массив JavaScript в node.js.

Так что, если файл выглядит так:

first line
two 
three
...
...

Я хотел бы иметь массив:

['first line','two','three', ... , ... ] 

Функция будет выглядеть так:

var array = load(filename); 

Поэтому идея загрузить все это как строку и затем разделить ее не приемлема.

15 ответов

Решение

Если вы можете поместить окончательные данные в массив, то разве вы не сможете поместить их в строку и разделить, как было предложено? В любом случае, если вы хотите обработать файл по одной строке за раз, вы также можете попробовать что-то вроде этого:

var fs = require('fs');

function readLines(input, func) {
  var remaining = '';

  input.on('data', function(data) {
    remaining += data;
    var index = remaining.indexOf('\n');
    while (index > -1) {
      var line = remaining.substring(0, index);
      remaining = remaining.substring(index + 1);
      func(line);
      index = remaining.indexOf('\n');
    }
  });

  input.on('end', function() {
    if (remaining.length > 0) {
      func(remaining);
    }
  });
}

function func(data) {
  console.log('Line: ' + data);
}

var input = fs.createReadStream('lines.txt');
readLines(input, func);

РЕДАКТИРОВАТЬ: (в ответ на комментарий phopkins) Я думаю (по крайней мере, в более новых версиях) подстрока не копирует данные, но создает специальный объект SlicedString (с первого взгляда на исходный код v8). В любом случае, здесь есть модификация, которая избегает упомянутой подстроки (проверено на файле стоимостью в несколько мегабайт "Вся работа и отсутствие игры делает Джека скучным мальчиком"):

function readLines(input, func) {
  var remaining = '';

  input.on('data', function(data) {
    remaining += data;
    var index = remaining.indexOf('\n');
    var last  = 0;
    while (index > -1) {
      var line = remaining.substring(last, index);
      last = index + 1;
      func(line);
      index = remaining.indexOf('\n', last);
    }

    remaining = remaining.substring(last);
  });

  input.on('end', function() {
    if (remaining.length > 0) {
      func(remaining);
    }
  });
}

Синхронный:

var fs = require('fs');
var array = fs.readFileSync('file.txt').toString().split("\n");
for(i in array) {
    console.log(array[i]);
}

Асинхронный:

var fs = require('fs');
fs.readFile('file.txt', function(err, data) {
    if(err) throw err;
    var array = data.toString().split("\n");
    for(i in array) {
        console.log(array[i]);
    }
});

Использование модуля readline для Node.js.

var fs = require('fs');
var readline = require('readline');

var filename = process.argv[2];
readline.createInterface({
    input: fs.createReadStream(filename),
    terminal: false
}).on('line', function(line) {
   console.log('Line: ' + line);
});

Js :

var array = fs.readFileSync('file.txt', 'utf8').split('\n');

ts:

var array = fs.readFileSync('file.txt', 'utf8').toString().split('\n');

По сути, это сработает: .replace(/\r\n/g,'\n').split('\n'). Это работает на Mac, Linux и Windows.

Фрагменты кода

Синхронный:

const { readFileSync } = require('fs');

const array = readFileSync('file.txt').toString().replace(/\r\n/g,'\n').split('\n');

for(let i of array) {
    console.log(i);
}

Асинхронный:

С помощью API fs.promises, который предоставляет альтернативный набор методов асинхронной файловой системы, которые возвращают объекты Promise вместо использования обратных вызовов. (Нет необходимости обещать, вы также можете использовать async-await с этим, доступным на Node.js версии 10.0.0 и после)

const { readFile } = require('fs').promises;

readFile('file.txt', function(err, data) {
    if(err) throw err;

    const arr = data.toString().replace(/\r\n/g,'\n').split('\n');

    for(let i of arr) {
        console.log(i);
    }
});

Подробнее о \r & \n здесь: \ r \ n, \ r и \ n в чем разница между ними?

Используйте readline ( документация). Вот пример чтения файла CSS, анализа значков и записи их в json

var results = [];
  var rl = require('readline').createInterface({
    input: require('fs').createReadStream('./assets/stylesheets/_icons.scss')
  });


  // for every new line, if it matches the regex, add it to an array
  // this is ugly regex :)
  rl.on('line', function (line) {
    var re = /\.icon-icon.*:/;
    var match;
    if ((match = re.exec(line)) !== null) {
      results.push(match[0].replace(".",'').replace(":",''));
    }
  });


  // readline emits a close event when the file is read.
  rl.on('close', function(){
    var outputFilename = './icons.json';
    fs.writeFile(outputFilename, JSON.stringify(results, null, 2), function(err) {
        if(err) {
          console.log(err);
        } else {
          console.log("JSON saved to " + outputFilename);
        }
    });
  });

file.lines с пакетом JFile

Псевдо

var JFile=require('jfile');

var myF=new JFile("./data.txt");
myF.lines // ["first line","second line"] ....

Не забудь раньше:

npm install jfile --save

С BufferedReader, но функция должна быть асинхронной:

var load = function (file, cb){
    var lines = [];
    new BufferedReader (file, { encoding: "utf8" })
        .on ("error", function (error){
            cb (error, null);
        })
        .on ("line", function (line){
            lines.push (line);
        })
        .on ("end", function (){
            cb (null, lines);
        })
        .read ();
};

load ("file", function (error, lines){
    if (error) return console.log (error);
    console.log (lines);
});

Чтобы прочитать большой файл в массив, вы можете прочитать строку за строкой или фрагмент за фрагментом.

построчно обратитесь к моему ответу здесь

var fs = require('fs'),
    es = require('event-stream'),

var lines = [];

var s = fs.createReadStream('filepath')
    .pipe(es.split())
    .pipe(es.mapSync(function(line) {
        //pause the readstream
        s.pause();
        lines.push(line);
        s.resume();
    })
    .on('error', function(err) {
        console.log('Error:', err);
    })
    .on('end', function() {
        console.log('Finish reading.');
        console.log(lines);
    })
);

кусок за куском обратитесь к этой статье

var offset = 0;
var chunkSize = 2048;
var chunkBuffer = new Buffer(chunkSize);
var fp = fs.openSync('filepath', 'r');
var bytesRead = 0;
while(bytesRead = fs.readSync(fp, chunkBuffer, 0, chunkSize, offset)) {
    offset += bytesRead;
    var str = chunkBuffer.slice(0, bytesRead).toString();
    var arr = str.split('\n');

    if(bytesRead = chunkSize) {
        // the last item of the arr may be not a full line, leave it to the next chunk
        offset -= arr.pop().length;
    }
    lines.push(arr);
}
console.log(lines);

Это вариант ответа выше @mtomis.

Это создает поток линий. Он генерирует события data и end, что позволяет вам обрабатывать конец потока.

var events = require('events');

var LineStream = function (input) {
    var remaining = '';

    input.on('data', function (data) {
        remaining += data;
        var index = remaining.indexOf('\n');
        var last = 0;
        while (index > -1) {
            var line = remaining.substring(last, index);
            last = index + 1;
            this.emit('data', line);
            index = remaining.indexOf('\n', last);
        }
        remaining = remaining.substring(last);
    }.bind(this));

    input.on('end', function() {
        if (remaining.length > 0) {
            this.emit('data', remaining);
        }
        this.emit('end');
    }.bind(this));
}

LineStream.prototype = new events.EventEmitter;

Используйте это как обертку:

var lineInput = new LineStream(input);

lineInput.on('data', function (line) {
    // handle line
});

lineInput.on('end', function() {
    // wrap it up
});

Использование Node.js v8 или новее имеет новую функцию, которая преобразует обычную функцию в асинхронную функцию.

util.promisify

Это потрясающая особенность. Вот пример разбора 10000 чисел из txt-файла в массив с подсчетом инверсий с использованием сортировки слиянием по числам.

// read from txt file
const util = require('util');
const fs = require('fs')
fs.readFileAsync = util.promisify(fs.readFile);
let result = []

const parseTxt = async (csvFile) => {
  let fields, obj
  const data = await fs.readFileAsync(csvFile)
  const str = data.toString()
  const lines = str.split('\r\n')
  // const lines = str
  console.log("lines", lines)
  // console.log("str", str)

  lines.map(line => {
    if(!line) {return null}
    result.push(Number(line))
  })
  console.log("result",result)
  return result
}
parseTxt('./count-inversion.txt').then(() => {
  console.log(mergeSort({arr: result, count: 0}))
})

Я просто хочу добавить @finbarr отличный ответ, небольшое исправление в асинхронном примере:

Асинхронный:

var fs = require('fs');
fs.readFile('file.txt', function(err, data) {
    if(err) throw err;
    var array = data.toString().split("\n");
    for(i in array) {
        console.log(array[i]);
    }
    done();
});

@MadPhysicist, done () - это то, что освобождает асинхронность. вызов.

У меня была такая же проблема, и я решил ее с помощью модуля построчно

https://www.npmjs.com/package/line-by-line

По крайней мере, для меня работает как брелок, как в синхронном, так и в асинхронном режиме.

Кроме того, проблему с окончанием строк, не заканчивающимся \n, можно решить с помощью опции:

{ encoding: 'utf8', skipEmptyLines: false }

Синхронная обработка строк:

var LineByLineReader = require('line-by-line'),
    lr = new LineByLineReader('big_file.txt');

lr.on('error', function (err) {
    // 'err' contains error object
});

lr.on('line', function (line) {
    // 'line' contains the current line without the trailing newline character.
});

lr.on('end', function () {
    // All lines are read, file is closed now.
}); 

Чтобы поместить каждую строку как элемент внутри массива, в Node.js v18.11.0 была добавлена ​​новая функция для чтения файлов построчно.

  • filehandle.readLines([параметры])

Вот как вы используете это с текстовым файлом, который вы хотите прочитать, и поместить каждую строку в массив

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

Чтобы понять больше, прочитайте документацию Node.js, вот ссылка для файловой системы readlines():https://nodejs.org/api/fs.html#filehandlereadlinesoptions

Другой ответ с использованием пакета npm. Вnexlinepackage позволяет асинхронно читать файл построчно:

      "use strict";

import fs from 'fs';
import nexline from 'nexline';

const lines = [];
const reader = nexline({
    input: fs.createReadStream(`path/to/file.ext`)
});

while(true) {
    const line = await reader.next();
    if(line === null) break; // line is null if we reach the end
    if(line.length === 0) continue; // Ignore empty lines
    
    // Process the line here - below is just an example
    lines.push(line);
}

Этот подход будет работать, даже если ваш текстовый файл больше, чем максимально допустимая длина строки, тем самым избегая Error: Cannot create a string longer than 0x1fffffe8 charactersошибка.

Другие вопросы по тегам