首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >在Nodejs中解析大型JSON文件

在Nodejs中解析大型JSON文件
EN

Stack Overflow用户
提问于 2012-08-09 06:24:26
回答 9查看 126.7K关注 0票数 119

我有一个以JSON格式存储许多JavaScript对象的文件,我需要读取该文件,创建每个对象,并对它们执行一些操作(在我的例子中,将它们插入到一个db中)。JavaScript对象可以用以下格式表示:

格式A:

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

格式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()呢?

理想情况下,每个对象将作为单独的数据块读取,但我不确定如何做到这一点。

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!");
});*/

注意,我希望阻止将整个文件读取到内存中。时间效率对我来说并不重要。是的,我可以尝试一次读取多个对象,然后一次插入所有对象,但这是一个性能调整-我需要一种保证不会导致内存过载的方法,而不管文件中包含多少对象。

我可以选择使用FormatAFormatB,也可以选择其他方式,请在您的答案中指明。谢谢!

EN

回答 9

Stack Overflow用户

回答已采纳

发布于 2012-08-09 07:26:12

要逐行处理文件,只需将文件读取和处理该输入的代码解耦即可。你可以通过缓冲你的输入直到换行符来实现这一点。假设我们每行有一个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循环)。通过这种方式,我们可以处理当前块中读取的所有行。

最后,每个输入行调用一次process。如果存在,它会去掉回车字符(以避免行尾问题-- LF ),然后调用一行中的JSON.parse。此时,您可以对对象执行所需的任何操作。

请注意,JSON.parse对它接受的输入内容非常严格;您必须用双引号将标识符和字符串值引起来。换句话说,您必须使用{"name":"thing1"}{name:'thing1'}将抛出一个错误。

因为一次只有一块数据在内存中,所以这将是非常高效的内存。它也会非常快。快速测试显示,我在15ms内处理了10,000行数据。

票数 91
EN

Stack Overflow用户

发布于 2012-08-09 06:49:02

就在我想写一个流式JSON解析器会很有趣的时候,我还想也许我应该快速搜索一下,看看是否有可用的解析器。

事实证明是这样的。

因为我刚发现它,我显然没有用过它,所以我不能评论它的质量,但我很有兴趣知道它是否有效。

考虑到以下Javascript和_.isString,它确实可以工作

stream.pipe(JSONStream.parse('*'))
  .on('data', (d) => {
    console.log(typeof d);
    console.log("isString: " + _.isString(d))
  });

如果流是一个对象数组,这将在对象进入时记录对象。因此,唯一要缓冲的是一次只能缓冲一个对象。

票数 42
EN

Stack Overflow用户

发布于 2014-07-12 13:51:21

从2014年10月起,您只需执行以下操作(使用JSONStream) - https://www.npmjs.org/package/JSONStream

var fs = require('fs'),
    JSONStream = require('JSONStream'),

var getStream() = function () {
    var jsonData = 'myData.json',
        stream = fs.createReadStream(jsonData, { encoding: 'utf8' }),
        parser = JSONStream.parse('*');
    return stream.pipe(parser);
}

getStream().pipe(MyTransformToDoWhateverProcessingAsNeeded).on('error', function (err) {
    // handle any errors
});

要使用工作示例进行演示,请执行以下操作:

npm install JSONStream event-stream

data.json:

{
  "greeting": "hello world"
}

hello.js:

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

var getStream = function () {
    var jsonData = 'data.json',
        stream = fs.createReadStream(jsonData, { encoding: 'utf8' }),
        parser = JSONStream.parse('*');
    return stream.pipe(parser);
};

getStream()
    .pipe(es.mapSync(function (data) {
        console.log(data);
    }));
$ node hello.js
// hello world
票数 36
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11874096

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档