道招

大文件的文本内容替换方案——流式替换

如果您发现本文排版有问题,可以先点击下面的链接切换至老版进行查看!!!

大文件的文本内容替换方案——流式替换

常规的文本替换很简单,直接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,达到了我们想要的效果。

更新时间:
上一篇:研究学习华为IAP严谨的支付过程下一篇:到顶了

相关文章

关注道招网公众帐号
友情链接
消息推送
道招网关注互联网,分享IT资讯,前沿科技、编程技术,是否允许文章更新后推送通知消息。
允许
不用了