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

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

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

first line
two 
three
...
...

我会得到数组:

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

函数如下所示:

var array = load(filename); 

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

【问题讨论】:

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

标签: javascript node.js


【解决方案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);
    });
    

    【讨论】:

    【解决方案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?

              【讨论】:

                最近更新 更多