web-dev-qa-db-ja.com

あるファイルの行を別のファイルのパターンに一致するように印刷する

40.000行を超えるファイル(file1)があり、file2のパターンに一致する行(約6000行)を抽出します。私はこのようにgrepを使用していますが、非常に遅いです:grep -f file2 file1 > out

awkまたはsedを使用してこれを行うより速い方法はありますか?

これが私のファイルからの抜粋です。

File1:
scitn003869.2| scign003869 CGCATGTGTGCATGTATTATCGTATCCCTTG
scitn007747.1| scign007747  CACGCAGACGCAGTGGAGCATTCCAGGTCACAA
scitn003155.1| scign003155  TAAAAATCGTTAGCACTCGCTTGGTACACTAAC
scitn018252.1| scign018252  CGTGTGTGTGCATATGTGTGCATGCGTG
scitn004671.2| scign004671  TCCTCAGGTTTTGAAAGGCAGGGTAAGTGCT
File2:
scign000003
scign000004
scign000005
scign004671
scign000013

`

16
Jon

grep -Fwf file2 file1 > outをお試しください

-Fオプションはプレーンストリングマッチングを指定するため、正規表現エンジンを使用する必要がなく、より高速になるはずです。

27
glenn jackman

これをawkで行う方法は次のとおりです。

awk 'NR==FNR{pats[$0]; next} $2 in pats' File2 File1

60,000行のFile1(ファイル1が8000回繰り返された)と6,000ファイル2(ユーザーが1200回繰り返した)を使用します。

$ time grep -Fwf File2 File1 > ou2

real    0m0.094s
user    0m0.031s
sys     0m0.062s

$ time awk 'NR==FNR{pats[$0]; next} $2 in pats' File2 File1 > ou1

real    0m0.094s
user    0m0.015s
sys     0m0.077s

$ diff ou1 ou2

つまり、grepと同じくらい高速です。ただし、awkソリューションでは、照合する特定のフィールドを選択できるため、File2のいずれかがFile1の他の場所に表示されても、誤って一致することはありません。また、ターゲット文字列がさまざまな長さで、たとえば「scign000003」を「scign0000031」に一致させたくない場合は、一度にフィールド全体を照合することもできます(ただし、grepの-wは同様の保護を提供します)。

完全を期すために、elsethreadに投稿された他のawkソリューションのタイミングを以下に示します。

$ time awk 'BEGIN{i=0}FNR==NR{a[i++]=$1;next}{for(j=0;j<i;j++)if(index($0,a[j]))print $0}' File2 File1 > ou3

real    3m34.110s
user    3m30.850s
sys     0m1.263s

マークが投稿したPerlスクリプトのタイミングは次のとおりです。

$ time ./go.pl > out2

real    0m0.203s
user    0m0.124s
sys     0m0.062s
14
Ed Morton

あなたはこのawkで試すことができます:

awk 'BEGIN{i=0}
FNR==NR { a[i++]=$1; next }
{ for(j=0;j<i;j++)
    if(index($0,a[j]))
        {print $0;break}
}' file2 file1

FNR==NRの部分は、中括弧で後に続くものが最初の入力ファイル(file2)を処理するときにのみ適用されることを指定します。そして、あなたが探しているすべての単語を配列a[]に保存するように言っています。中括弧の2番目のセットのビットは2番目のファイルの処理に適用されます...各行が読み込まれると、それはa[]のすべての要素と比較され、見つかった場合はその行が出力されます。それはすべての人々です!

6
Mark Setchell

単に学習のために:同じ問題を解決していて、さまざまな解決策を思いついた(read $lineループなど)。上記で見つかったgrepワンライナーにたどり着いたとき、私はまだ間違った出力を得ていました。次に、PATTERNファイルに2行の末尾行があることがわかりました... grepはデータベースからすべての行を取得しました。道徳:末尾のスペース/行を確認してください。また、数百のパターンを持つ非常に大きなデータセットに対してコマンドを実行したところ、timeは数え切れませんでした。

4
adrien

ちょうど楽しみのために、これはPerlバージョンです:

#!/usr/bin/Perl
use strict;
use warnings;
my %patterns;
my $srch;

# Open file and get patterns to search for
open(my $fh2,"<","file2")|| die "ERROR: Could not open file2";
while (<$fh2>)
{
   chop;
   $patterns{$_}=1;
}

# Now read data file
open(my $fh1,"<","file1")|| die "ERROR: Could not open file1";
while (<$fh1>)
{
   (undef,$srch,undef)=split;
   print $_ if defined $patterns{$srch};
}

以下に、Edのファイル作成方法ごとに60,000行のfile1と6,000行のfile2を使用したタイミングをいくつか示します。

time awk 'NR==FNR{pats[$0]; next} $2 in pats' file2 file1 > out
real    0m0.202s
user    0m0.197s
sys     0m0.005s

time ./go.pl > out2
real    0m0.083s
user    0m0.079s
sys     0m0.004s
4
Mark Setchell