после некоторых проб и ошибок я нашел одно решение:
для большого файла, нам нужно использовать stream
: например, fs.createReadStream('...')
для юникода, нам нужно чтобы использовать флаг encoding
: fs.createReadStream('/path/to/file', { encoding: 'utf8', fd: null })
для подсчета байтовой позиции, нам нужно преобразовать его в буфер, как Buffer.from(stream.read()).length
полный пример для индексации текстового файла в 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));