查找并替换大文件

时间:2016-09-02 08:24:55

标签: bash perl awk sed grep

屏蔽问题:我需要从单个大文本文件(input.txt,100 + MB)中找到并屏蔽(即,用“XXX”替换)某些术语(单词/表达式)。我需要找到的术语(10K +)保存在单个文件(to_mask.txt)中。如何以有效的方式执行此操作?

我正在考虑分两步执行此操作:首先找到实际包含术语

的行
grep -Ff to_mask.txt -o -n input.txt

接下来浏览输出并进行实际替换(术语 - >“XXX”)。

这看起来有点乏味,可以用更聪明的方式完成吗?

欢迎任何基本命令组合(grep,sed,awk,one-line-perl)!

更新

MarcoS,Kenavoz,Ed Morton和Sobrique都提供了有效的解决方案,谢谢! 我选择Sobrique的解决方案作为我接受的解决方案,因为它比我的数据快得多。它可能无法处理某些特殊情况,但我确信它可以扩展到能够这样做,而且它可以完成当前工作中的工作。

UPDATE2:

供参考,以下是Kenavoz提供的解决方案:

sed -f <(sed 's~^~s\~~;s~$~\~XXX\~~' to_mask.txt) input.txt

3 个答案:

答案 0 :(得分:1)

你可以尝试:

while read mask; do sed -i "s/$mask/XXX/g" input.txt; done < to_mask.txt

可能不是世界上最有效的解决方案,但它应该做的工作...... :-)
作为奖励,它只使用shell和sed命令......

<强>更新

这是一个更快的解决方案(它只写一次大input.txt个文件)。 它首先构建一个fullmask变量,作为由|OR运算符)分隔的所有掩码的串联。 虽然,我发现它不如第一个发布的解决方案明确......: - )

fullmask=""; cat to_mask.txt | while read mask; do fullmask="$fullmask|$mask"; done && sed -i "s/$fullmask/XXX/g" input.txt

请不要我没有测试过这个解决方案,它可能包含一些问题...
更重要的是,它假定to_mask.txt不包含任何|或任何/个字符......

更新2

抱歉! sed不支持正则表达式,替换...... :-(
我使用perl提出了这个解决方案,更加丑陋,但绝对有效(只是在一个简单的用例上测试过):

 fullmask="("; while read mask; do if [ "$fullmask" != "(" ]; then fullmask="$fullmask|$mask"; else fullmask="$fullmask$mask"; fi; done < to_mask.txt; fullmask="$fullmask)"; perl -p -i -e "s/$fullmask/XXX/g" input.txt

答案 1 :(得分:1)

我想我会像这样解决它:

#!/usr/bin/perl
use strict;
use warnings;

#read the mask file into memory. 
open ( my $mask, '<', "to_mask.txt" ) or die $!;
chomp ( my @terms = <$mask> ); 
close ( $mask );
#build a really big regex
#map quotemeta means handling metachars. 
#if you _know_ there are none, then you can omit this.
#or if you actually want to be able to use regex in your terms file. 
my $mega_regex = join "|", map { quotemeta } @terms; 

   #compile it into a non-capturing regex, and use \b to anchor on word boundaries. 
   #You don't want to be filtering out Scunthorpe ... 
   $mega_regex = qr/\b(?:$mega_regex)\b/;

#<> means iterate 'stdin' or 'files specified on command line'. 
#just like how grep/sed/awk does it
while ( <> ) { 
    s/$mega_regex/XXX/g;
    print;  #to STDOUT
}

答案 2 :(得分:1)

你只需要1个命令:

awk 'NR==FNR{t=(t?t"|":"")$0;next} {gsub(t,"XXX")} 1' to_mask.txt input.txt