大文件的文本内容替换方案——流式替换
如果您发现本文排版有问题,可以先点击下面的链接切换至老版进行查看!!!
大文件的文本内容替换方案——流式替换
常规的文本替换很简单,直接replace替换即可,但是遇到大文件就麻烦了,直接获取全部的文本内容不现实,太占用内存甚至可能导致程序崩溃,这个时候就需要流式读取文件内容,也就是一次读取文件的一部分,跟蚂蚁搬家似的,那样每次占用内容的空间就不大了,程序能一直正常处理。
流式处理的话需要将每次读取一定长度(必须需要大于待替换的文本长度)的数据块,可以取一个固定值,比如1M,足够了。
流式处理可能存在的问题
采用分块处理会带来一个问题,如果取出的数据的开头或结尾刚好把一个待替换的文本切开了怎么,如图所示
第三个“待替换文本”就刚好一部分在“数据块1”,另一部分在“数据块2”,在处理“数据块1”的文本替换的时候就只能找到两个符合条件的文本,也就只能替换掉两个,在处理“数据块2(假设和数据1类似)”的文本替换的时候也只能找到并替换到两个符合条件的文本,但实际上就是数据1和数据2整体而言,少替换了一个“待替换文本”——“待替换文本3”。
解决问题思路
这种场景我们应该怎么处理呢?
在处理“数据块1”的时候,我们可以确定的是先把前面两个好处理的替换文本给替换掉,然后余下不够分的部分和后面的“数据块2”合并在一起成为“新的数据块2”,然后跟处理“数据块1”的方法一样,对“新的数据块2”进行替换操作,此时“待替换文本3”就能被找到并替换了,如果也有余下的部分,我们也将它合并到下面的“新的数据块3”,以此类推即可。
解决问题实现
具体的实现方式如下
const fs = require('fs');
const { Transform } = require('stream');
class ReplaceTransform extends Transform {
constructor(searchRegex, replaceString, options) {
super(options);
this.searchRegex = searchRegex;
this.replaceString = replaceString;
this.tailPiece = '';
this.replaceTailPieceLength = Math.max(...[...searchRegex.toString().matchAll(/\((?!\?)/g)].map(match => match.index)) || 0;
}
_transform(chunk, encoding, callback) {
const piece = this.tailPiece + chunk.toString();
const pieces = piece.split(this.searchRegex);
const piecesLength = pieces.length;
if (piecesLength > 1) {
console.log('piecesLength ~ ', piecesLength);
}
const matchPiece = pieces.pop();
this.tailPiece = matchPiece.slice(-this.replaceTailPieceLength);
const toSend = pieces.join(this.replaceString) + (pieces.length > 0 ? this.replaceString : '') + matchPiece.slice(0, -this.replaceTailPieceLength);
this.push(toSend);
callback();
}
_flush(callback) {
this.push(this.tailPiece.replace(this.searchRegex, this.replaceString));
callback();
}
}
async function replaceTextInFile(filePath, oldRegex, newText, blockSize = 1024 * 1024) {
const tempFilePath = filePath + '.tmp';
const readStream = fs.createReadStream(filePath, { encoding: 'utf8' });
const writeStream = fs.createWriteStream(tempFilePath, { encoding: 'utf8' });
return new Promise((resolve, reject) => {
// 管道流操作
readStream
.pipe(new ReplaceTransform(oldRegex, newText))
.pipe(writeStream)
.on('finish', () => {
// 替换完成后,替换原文件
fs.renameSync(tempFilePath, filePath);
resolve();
})
.on('error', (err) => {
console.error('Error replacing text:', err);
reject(err)
});
})
}
replaceTextInFile('bigText.txt', /ppt文件/, 'powerPoint演示稿');
此时我们将大文件bigText.txt
里面全部的ppt文件
替换成了powerPoint演示稿
写入bigText.txt.tmp
这个临时文件,然后再将它更名为bigText.txt
,达到了我们想要的效果。
- 分类:
- Web前端
更新时间:
上一篇:研究学习华为IAP严谨的支付过程下一篇:到顶了