关于如何使用NodeJS读取utf-8编码的文本文件有很多答案;但是,我的问题是如何读取大文件。在这里,“大”表示超出了存储能力,例如64GB。
说我们有一个64GB的JSON文件,其中文件包含utf-8字符;如何获得像locale.ja.test
= 測定
这样的pathKey值;例如,如果我们有像{ "a": { "b": { "c": 1 } } }
这样的JSON对象,则pathKey a.b.c
的值引用值1
。
如果是ascii编码的文本,我们可以简单地将文件分成几部分;例如,我们读取文件100MB x 100MB,并使用parse(previousStat, block) -> stat
之类的解析器;但是对于utf-8编码的文本,问题在于,如果将文件分割成几个部分,对于某些特殊情况,我们可能会将一个字符分割成2个块。像...\0x88\0x12...
-> [...\x88]
,[\x12...]
。
如何正确读取大型utf-8编码的文本文件?谢谢!
注意:JSON文件可以写在一行中,这意味着readline
可能无济于事。
类似的问题,没有答案:
经过一番尝试和错误之后,我找出了一种解决方案:
对于大文件,我们需要使用stream
:例如fs.createReadStream('...')
对于Unicode,我们需要使用标志encoding
:fs.createReadStream('/path/to/file', { encoding: 'utf8', fd: null })
为了计算字节位置,我们需要像Buffer.from(stream.read()).length
那样将其转换为Buffer。>
在3-GRAM和2-GRAM中为文本文件建立索引的完整示例:
这是测试-> [Thi, his, is , s i, is , s t, te, tes, est]
和[Th, hi, is, s , i, is, s , t, te, es, st]
const i_s = require('stream');
const i_fs = require('fs');
function buildIndexer(I) {
// I = { indexStat: { cur: 0, gram: [] }, index: { gram2: {}, gram3: {} } }
return i_s.Transform({
transform: (chunk, _encoding, next) => {
// _encoding should be 'utf8'
const N = chunk.length;
for (let i = 0; i < N; i++) {
const ch = chunk[i];
const len = Buffer.from(ch).length;
I.indexStat.gram.push(ch);
let gn = I.indexStat.gram.length - 1;
if (gn > 3) {
I.indexStat.gram.shift();
gn --;
}
if (gn >= 2) {
const g2 = `${I.indexStat.gram[gn-2]}${I.indexStat.gram[gn-1]}`;
I.index.gram2[g2] = I.index.gram2[g2] || [];
I.index.gram2[g2].push(I.indexStat.cur - Buffer.from(g2).length);
}
if (gn >= 3) {
const g3 = `${I.indexStat.gram[gn-3]}${I.indexStat.gram[gn-2]}${I.indexStat.gram[gn-1]}`;
I.index.gram3[g3] = I.index.gram3[g3] || [];
I.index.gram3[g3].push(I.indexStat.cur - Buffer.from(g3).length);
}
next(null, chunk);
},
decodeStrings: false,
encoding: 'utf8',
});
}
const S = i_fs.createReadStream('/path/to/file', { encoding: 'utf8', fd: null });
const I = { indexStat: { cur: 0, gram: [] }, index: { gram2: {}, gram3: {} } }
const T = buildIndexer(I);
S.pipe(T);
S.on('finish', () => S.close());
T.on('finish', () => console.log(I.index));