【问题标题】:Nodejs - removing substring from a huge fileNodejs - 从大文件中删除子字符串
【发布时间】:2020-06-15 20:11:59
【问题描述】:

我需要从文件中删除一个子字符串(仅出现在文件的特定已知行中)。

有简单的解决方案,将所有文件数据读取到一个字符串,删除子字符串,然后将固定数据写入文件。

这是我在这里找到的代码:

Node js - Remove string from text file

var data = fs.readFileSync('banlist.txt', 'utf-8');
var newValue = data.replace(new RegEx("STRING_TO_REMOVE"), '');
fs.writeFileSync('banlist.txt', newValue, 'utf-8');

我的问题是,文件很大——多达十亿行日志,所以我无法将所有内容读入内存。

【问题讨论】:

  • 查看如何在此处stackoverflow.com/questions/6156501/… 创建逐行读取流,然后您只需将写入流创建到另一个文件并将您的读取流通过管道传输到写入流中。

标签: javascript node.js


【解决方案1】:

为什么不是简单的转换流和replace()replace 可以将回调作为第二个参数,即.replace(/bad1|bad2|bad3/g, filterWords),以防您需要替换单词而不是完全删除它们。

const fs = require("fs")
const { pipeline, Transform } = require("stream")
const { join } = require("path")

const readFile = fs.createReadStream("./words.txt")
const writeFile = fs.createWriteStream(
  join(__dirname, "words-filtered.txt"),
  "utf8"
)

const transformFile = new Transform({
  transform(chunk, enc, next) {
    let c = chunk.toString().replace(/bad/g, "replaced")
    this.push(c)
    next()
  },
})

pipeline(readFile, transformFile, writeFile, (err) => {
  if (err) {
    console.log(err.message)
  }
})

【讨论】:

    【解决方案2】:

    https://nodejs.org/api/fs.html#fs_fs_read_fd_buffer_offset_length_position_callback

    不要一次读取整个文件...读取它的一小块缓冲...并使用该缓冲块查找您的输入...然后增加您的缓冲区起始位置并再次执行...会建议让每个缓冲区不要从前一个缓冲区的末尾开始......但至少重叠正在寻找的数据的预期大小,这样您就不会遇到一半的数据位于一个缓冲区的末尾而另一半位于开头其他的

    【讨论】:

    • 公平地说...如果数据很大,我建议将其移动到数据库而不是平面文件
    【解决方案3】:

    您可以使用file read stream。但是,您必须找到一种方法来检测读取的数据是否仅包含部分结果。

    【讨论】:

      【解决方案4】:

      您可能想要做的是使用流,以便在部分读取之后进行写入。这个例子可能对你有用。您需要将输出文本文件“.tmp”复制到原始文件中才能在您的问题中获得相同的行为。它通过读取一个块然后查看您是否遇到新行来工作。然后它处理该行,将其写入,然后将其从缓冲区中删除。这应该有助于解决您的记忆问题。

      var fs = require("fs");
      var readStream = fs.createReadStream("./BFFile.txt", { encoding: "utf-8" });
      var writeStream = fs.createWriteStream("./BFFile.txt.tmp");
      
      const STRING_TO_REMOVE = "badword";
      var buffer = ""
      
      readStream.on("data", (chunk) => {
          buffer += chunk;
          var indexOfNewLine = buffer.search("\n");
          while (indexOfNewLine !== -1) {
              var line = buffer.substring(0, indexOfNewLine + 1);
              buffer = buffer.substring(indexOfNewLine + 1, buffer.length);
              line = line.replace(new RegExp(STRING_TO_REMOVE), "");
              writeStream.write(line);
              indexOfNewLine = buffer.search("\n");
          }
      })
      
      readStream.on("end", () => {
          buffer = buffer.replace(new RegExp(STRING_TO_REMOVE), "");
          writeStream.write(buffer);
          writeStream.close();
      })
      

      此解决方案有一些假设,例如数据为 UTF-8,每行可能只有 1 个坏词,每行都有一些文本(我没有测试),并且每行都结束以新行结尾,而不是其他行结尾。

      这是streams in Node 的文档 我的另一个想法是使用管道和转换流,但这似乎太过分了。

      【讨论】:

        【解决方案5】:

        您可以使用此代码来执行此操作。我正在使用 fs 流。它是为按块读取小内存中的大文件而创建的。 docs

        const fs = require('fs');
        
        const readStream = fs.createReadStream('./XXXXX');
        const writeStream = fs.createWriteStream('./XXXXXXX');
        
        readStream.on('data', (chunk) => {
          const data = chunk.toString().replace('STRING_TO_REMOVE', 'XXXXXX');
          writeStream.write(data);
        });
        
        readStream.on('end', () => {
          writeStream.close();
        });
        
        

        【讨论】:

          猜你喜欢
          • 2015-01-11
          • 2018-11-20
          • 2019-09-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多