我有一个文件,它以JSON的形式存储了许多JavaScript对象,我需要读取该文件,创建每个对象,并对它们进行处理(在我的例子中,将它们插入数据库).JavaScript对象可以用以下格式表示:

Format A:

[{name: 'thing1'},
....
{name: 'thing999999999'}]

还是Format B:

{name: 'thing1'}         // <== My choice.
...
{name: 'thing999999999'}

注意,...表示很多JSON对象.我知道我可以将整个文件读入内存,然后像这样使用JSON.parse():

fs.readFile(filePath, 'utf-8', function (err, fileContents) {
  if (err) throw err;
  console.log(JSON.parse(fileContents));
});

然而,文件可能非常大,我更喜欢使用流来完成这一点.我看到的流的问题是,文件内容在任何时候都可能被分解成数据块,那么如何在这样的对象上使用JSON.parse()呢?

理想情况下,每个对象都将被读取为一个单独的数据块,但我不确定是否为how to do that.

var importStream = fs.createReadStream(filePath, {flags: 'r', encoding: 'utf-8'});
importStream.on('data', function(chunk) {

    var pleaseBeAJSObject = JSON.parse(chunk);           
    // insert pleaseBeAJSObject in a database
});
importStream.on('end', function(item) {
   console.log("Woot, imported objects into the database!");
});*/

注意,我希望避免将整个文件读入内存.时间效率对我来说并不重要.是的,我可以try 一次读取多个对象,然后一次插入所有对象,但这是一个性能调整——我需要一种保证不会导致内存过载的方法,无论文件中包含多少对象.

我可以 Select 使用FormatAFormatB,或者其他,请在你的答案中详细说明.谢谢

推荐答案

要逐行处理文件,只需将文件的读取与作用于该输入的代码分离.你可以通过缓冲你的输入直到你到达一个换行符来实现这一点.假设每行有一个JSON对象(基本上是格式B):

var stream = fs.createReadStream(filePath, {flags: 'r', encoding: 'utf-8'});
var buf = '';

stream.on('data', function(d) {
    buf += d.toString(); // when data is read, stash it in a string buffer
    pump(); // then process the buffer
});

function pump() {
    var pos;

    while ((pos = buf.indexOf('\n')) >= 0) { // keep going while there's a newline somewhere in the buffer
        if (pos == 0) { // if there's more than one newline in a row, the buffer will now start with a newline
            buf = buf.slice(1); // discard it
            continue; // so that the next iteration will start with data
        }
        processLine(buf.slice(0,pos)); // hand off the line
        buf = buf.slice(pos+1); // and slice the processed data off the buffer
    }
}

function processLine(line) { // here's where we do something with a line

    if (line[line.length-1] == '\r') line=line.substr(0,line.length-1); // discard CR (0x0D)

    if (line.length > 0) { // ignore empty lines
        var obj = JSON.parse(line); // parse the JSON
        console.log(obj); // do something with the data here!
    }
}

每次文件流从文件系统接收数据时,都会将其存储在缓冲区中,然后调用pump.

如果缓冲区中没有换行符,pump将直接返回,而不做任何操作.下次流获取数据时,更多的数据(可能还有一个换行符)将被添加到缓冲区,然后我们将拥有一个完整的对象.

如果有换行符,pump从开始到换行符将缓冲区切掉,并将其交给process.然后再次判断缓冲区中是否有另一个换行符(while循环).通过这种方式,我们可以处理当前块中读取的所有行.

Finally, process is called once per input line. If present, it strips off the carriage return character (to avoid issues with line endings – LF vs CRLF), and then calls JSON.parse one the line. At this point, you can do whatever you need to with your object.

注意,JSON.parse对于它接受的输入是严格的;必须引用标识符和字符串值.换句话说,{name:'thing1'}将抛出一个错误;你必须使用{"name":"thing1"}.

因为一次内存中的数据不会超过一块,所以这将是非常高效的内存.它也会非常快.快速测试显示我在15毫秒内处理了10000行.

Node.js相关问答推荐

为高流量网站优化Node.js实时通知服务

如何使用MongoDB在Node.js 中向数组中添加项?

如何使用聚合管道交换键值对

无法使用Sequelize连接AWS RDS

如何在mongodb集合中设置数据限制?

我的Node.js应用程序没有将Mongoose方法findByIdAndDelete作为函数进行检测

条件内的表达式

使用 axios 和 Cheerio (Node js) 抓取 google 搜索

合并Shift对象数组以创建最终的排班表

使用 create-expo-app 时如何更改 webpack-config.js 中的哈希函数?

用户与mongoose 的完美搭配

Winston http 日志(log)级别的行为与 info 不同

tsc:当我上传 React+next js 和 node 项目时,在 heroku 找不到

`npm install` 以Killed结尾

node.js 是否支持yields ?

带有加密的nodejs中的SALT和HASH密码

如何在不全局安装的情况下在 Node REPL 中要求 node 模块?

nodejs - 如何读取和输出 jpg 图像?

在 Node.js 中混合使用 JavaScript 和 TypeScript

react-native run-android 无法识别