2016-09-02 5 views
2

マスキングの問題:大きなテキストファイル(input.txt、100+)から特定の用語(単語/表現)を見つけてマスクするMB)。見つけ出す必要のある用語(10K +)は、単一のファイル(to_mask.txt)に保存されます。効率的にこれを実行するにはどうすればよいですか?大きいファイルからの検索と置換

私は2つの段階でこれをやって考えていた:最初の実際

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

次の出力を通過し、実際の交換(用語 - >「XXX」)を行う条件を含む行を検索します。

これはちょっと面倒なようですが、スマートなやり方でできますか?

基本コマンド(grep、sed、awk、one-line-perl)の任意の組み合わせが歓迎です!

UPDATE:

マルコス、Kenavoz、エド・モートンとSobriqueすべてが、おかげで作業溶液を与えました! Sobriqueのソリューションは、私の受け入れられたソリューションとして選択しました。これは、私のデータが与えられた残りの部分よりもはるかに高速だったからです。いくつかの特別なケースは扱えないかもしれませんが、私はそれを可能にすることができると確信していますし、現在の状況で仕事をしています。

アップデート2:参考

、ここKenavozが提供するソリューションです:

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

を1つ以上のライナーでこの問題にアプローチします(特に、他のエンドユーザーが関与している場合)。また、マスクのフィルタリングは、多くの場合、0 b 5 C u r 1 + yで周回することが多く、コンピュータが見つけにくいのに対し、人間は読むことができる傾向があります。 –

+0

ありがとう@スローンを描く、私は多少スパムの問題に精通しています。しかし、私は、できるだけ少数のライブラリ(これはクライアントの側で実行される)でこれを実装する必要があるので、単純化のために効率を少し失うことがあります。 – vivasra

答えて

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 
} 
+0

ありがとうございます。非キャプチャ正規表現は動作していないようですが、私がコメントすれば、それは魅力的に機能します。ちなみに、マスク用語が "a"、 "b"、 "c"の場合、$ mega_regexをキャプチャしない場合は正しいですか?(?^:\ b(?:a | b | c)\ b) – vivasra

+0

Hrm、どちらのビットが機能していないのかわかりませんが、 '(?:pattern)'は非キャプチャグループです。この場合は重要ではありませんが、重複しています。 '(?^:\ b(?:fish | foo | moo)\ b)' – Sobrique

+0

どちらの構文もうまくいきません。しかし、あなたが言ったように、その部分は重複しているので、あなたのソリューションは完全に機能します。 – vivasra

1

あなたが試みることができる:

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

おそらくない、世界で最も効率的なソリューションを、しかしそれは仕事をしなければなりません:-)
ボーナスとしては、シェルそしてsedコマンドが...

UPDATE

これは(それが一度だけ大きな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がないと仮定し任意の|も任意の/文字が含まれてい...

UPDATE 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

これは、to_mask.txtファイルに、バックスラッシュする必要のある特殊文字を含む行が含まれていない限り正常に動作します。もしそうなら、to_mask.txtを先に操作して上記のように使用できるようにします。 – louigi600

+0

@ louigi600:あなたは正しいです... OPはデリミタを変更して '/'問題を避けることができます...しかしこれは 'to_mask.txt'ファイルの内容を知っていて、存在しない文字(あれば...)。 – MarcoS

+2

同じファイルを読み書きする回数は10,000回ですか?ああ。 – Sobrique

1

あなただけの1つのコマンド必要があります:私はあなたが最初の[スカンソープ問題](https://en.wikipedia.org/wiki/Scunthorpe_probの概念を理解しておくべきだと思い

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

ありがとう、上記のあなたのコメントのために! – vivasra