node.js:将文本文件读入数组。 (每一行都是数组中的一个项目。)

Posted

技术标签:

【中文标题】node.js:将文本文件读入数组。 (每一行都是数组中的一个项目。)【英文标题】:node.js: read a text file into an array. (Each line an item in the array.) 【发布时间】:2011-10-13 11:59:50 【问题描述】:

我想将一个非常非常大的文件读入 node.js 中的 javascript 数组。

那么,如果文件是这样的:

first line
two 
three
...
...

我会得到数组:

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

函数如下所示:

var array = load(filename); 

因此,将其全部加载为字符串然后拆分的想法是不可接受的。

【问题讨论】:

这个问题需要认真编辑和清理。它说将一个文本文件读入一个数组,但是当您阅读所有答案和 cmets 时,它实际上意味着一次读取一个文本文件。对于这个问题,@zswang 给出了迄今为止最好的答案。 是的,只需读取该文件并将每一行推入一个数组:***.com/a/34033928/1536309 【参考方案1】:

同步:

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

【讨论】:

谢谢。不幸的是,我不得不编辑我的问题。我的意思是如何读取一个巨大的文件。在字符串中读取它是不可接受的。 我发现在 Windows 制作的文件上执行此操作,我不得不拆分 \r\n 但这破坏了 Mac;所以更健壮; _array = string.replace(/\r\n/g,'\n').split('\n');为两者工作 +1 *** 中存在一些问题。现在,我经常在向下滚动太远后找到投票率很高的答案。这也是一个例子。它的投票率最高,但位于页面底部,排在最后。我认为 *** 需要改进他们的排序算法。 @shashwat 提出问题的人可以决定哪个是正确的答案。在这种情况下,他们需要一个针对大文件的流式解决方案,并且将整个文件放在一个字符串中是不可接受的。 SO 没什么错,真的。 @WillHancock 为什么不使用os.EOL 而不是那种怪物?【参考方案2】:

如果您可以将最终数据放入数组中,那么您是否也可以将其放入字符串中并按照建议进行拆分? 在任何情况下,如果您想一次处理一行文件,您也可以尝试以下操作:

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 源代码)。无论如何,这里有一个修改,它避免了提到的子字符串(在一个价值几兆字节的文件上测试了“All work and no play makes Jack a dull boy”):

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

【讨论】:

谢谢。回答你的问题:不,字符串太大了。 我在大约 2MB 左右的文件上尝试了这个,它非常慢,比将文件同步读取到字符串要慢得多。我认为问题是剩余的 = remaining.substring 行。 Node 的“数据”一次可能会给你很多,并且对每一行进行复制很快就会变成 O(n^2)。 @Finbar 的回答要好得多【参考方案3】:

带有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);
);

【讨论】:

【参考方案4】:

使用 Node.js readline module。

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

【讨论】:

遗憾的是,此解决方案存在一个问题:如果文件末尾没有\n,则不会得到最后一行!见:***.com/questions/18450197/… Node 已通过 \n ***.com/a/32599033/3763850 修复了该问题【参考方案5】:

这是@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
);

【讨论】:

您将以在实例之间共享事件结束。 var EventEmitter = require('events').EventEmitter; var util = require('util'); function GoodEmitter() EventEmitter.call(this); util.inherits(GoodEmitter, EventEmitter); 你到底在说什么实例? 尝试创建var li1 = new LineStream(input1), li2 = new LineStream(input2);,然后计算每个'end'被触发的次数 试过了。 'end' 为每个实例触发一次。 var fs = require('fs'); var input1 = fs.createReadStream('text.txt'); var ls1 = new LineStream(input1); ls1.on('data', function (line) console.log('1:line=' + line); ); ls1.on('end', function (line) console.log('1:fin'); ); var input2 = fs.createReadStream('text.txt'); var ls2 = new LineStream(input2); ls2.on('data', function (line) console.log('2:line=' + line); ); ls2.on('end', function (line) console.log('2:fin'); ); 输出:文本文件中的每一行都针对每个实例触发一次。 “结束”也是如此。【参考方案6】:

我遇到了同样的问题,我已经用模块逐行解决了

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

【讨论】:

【参考方案7】:

使用 readline (documentation)。这是一个读取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);
        
    );
  );

【讨论】:

【参考方案8】:

file.lines 和我的JFile package

var JFile=require('jfile');

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

别忘了之前:

npm install jfile --save

【讨论】:

这篇文章被正确地标记为垃圾邮件,因为你没有在帖子中指出 JFile 包是你的。请编辑您提到 JFile 的其他帖子以添加此信息。谢谢。 它被提到了 6 年。你有没有注意到第一句话中的“我的”这个词!! @StephenRauch 您是否注意到在第一句话中加入了“我的”一词。如果这是您的偏好,我可以简单地将它们全部标记为垃圾邮件。 哦!惊人的!谢谢@StephenRauch!这是非常古老的答案。 是的,但还有更多,我希望您以类似于我更新这个的方式更新它们。【参考方案9】:

我只想添加@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() 是释放异步的原因。打电话。

【讨论】:

【参考方案10】:

要将大文件读入数组,您可以逐行或逐块读取。

一行一行参考my answer here

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

逐块参考this article

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

【讨论】:

【参考方案11】:

使用 Node.js v8 或更高版本具有将普通函数转换为异步函数的新功能。

util.promisify

这是一个很棒的功能。这是将 txt 文件中的 10000 个数字解析为一个数组的示例,并使用对数字进行归并排序来计算反转。

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

【讨论】:

【参考方案12】:

js:

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

ts:

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

【讨论】:

为了防止上面抛出TypeError: fs.readFileSync(...).split is not a function,你应该像这样使用.toString():var array = fs.readFileSync('file.txt', 'utf8').toString().split('\n');【参考方案13】:

基本上这将完成这项工作:.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);

异步:

使用 fs.promises API,它提供了一组替代的异步文件系统方法,这些方法返回 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 and \n what is the difference between them?

【讨论】:

以上是关于node.js:将文本文件读入数组。 (每一行都是数组中的一个项目。)的主要内容,如果未能解决你的问题,请参考以下文章

将本地文本文件读入 JavaScript 数组 [重复]

VB6.0中如何实现逐行读入文本文件?

将 txt 文件的每一行读取到新的数组元素

C# - 将大 (150MB) 文本文件读入富文本框

打开一个文本文件,每次读取一行内容,将每一行作为String读入,并将Sring对象置入LinkedList中,按相反顺序打印出LinkList所有行.

将文件列读入数组