2012-07-15 1 views
10

file.contain.query.txtschnelle Alternative zu grep -f

ENST001 

ENST002 

ENST003 

file.to.search.in.txt

ENST001 90 

ENST002 80 

ENST004 50 

Da ENST003 hat keinen Eintrag in der 2. Datei und ENST004 hat kein Eintrag in der 1. Datei die erwartete Ausgabe lautet:

ENST001 90 

ENST002 80 

Multi-Abfrage in einer bestimmten Datei grep wir die folgende Regel tun:

seit ich wie 10000 Abfrage und fast 100000 Raw in file.to.search.in brauche sehr lange Zeit zu beenden (wie 5 Stunden). Gibt es eine schnelle Alternative zu grep -f?

+0

Was sind Ihre Bedürfnisse? Wollen Sie eine Datei mit den Zeilen der zweiten, gefiltert mit den Schlüsseln der ersten? –

+0

Ich habe die erwarteten Ergebnisse bearbeitet – user1421408

+1

Die Eingabeumleitung ist nicht notwendig. –

Antwort

10

Wenn Sie eine reine Perl Option möchten, Ihre Abfrage Dateischlüssel in einer Hash-Tabelle lesen, dann die Standardeingabe gegen diese Schlüssel überprüfen:

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

# build hash table of keys 
my $keyring; 
open KEYS, "< file.contain.query.txt"; 
while (<KEYS>) { 
    chomp $_; 
    $keyring->{$_} = 1; 
} 
close KEYS; 

# look up key from each line of standard input 
while (<STDIN>) { 
    chomp $_; 
    my ($key, $value) = split("\t", $_); # assuming search file is tab-delimited; replace delimiter as needed 
    if (defined $keyring->{$key}) { print "$_\n"; } 
} 

Sie würden es wie so verwenden:

lookup.pl < file.to.search.txt 

eine Hash-Tabelle eine angemessene Menge an Speicher nehmen, sondern sucht viel sind schneller (Hash-Tabellen-Lookups sind in konstanter Zeit), was praktisch ist, da Sie 10-fach mehr Schlüssel zum Nachschlagen als zum Speichern haben.

+2

Dies ist Ferrari im Vergleich mit grep -f .. Danke – user1421408

+0

Perfekte Lösung; +1 –

5

Dies kann Perl-Code hilft Ihnen:

use strict; 
open my $file1, "<", "file.contain.query.txt" or die $!; 
open my $file2, "<", "file.to.search.in.txt" or die $!; 

my %KEYS =(); 
# Hash %KEYS marks the filtered keys by "file.contain.query.txt" file 

while(my $line=<$file1>) { 
    chomp $line; 
    $KEYS{$line} = 1; 
} 

while(my $line=<$file2>) { 
    if($line =~ /(\w+)\s+(\d+)/) { 
     print "$1 $2\n" if $KEYS{$1}; 
    } 
} 

close $file1; 
close $file2; 
+0

Sie haben vergessen, den Rückgabewert des Syscalls zu überprüfen. – tchrist

1

Mysql:

die Daten in Mysql importieren oder ähnlich wird eine immense Verbesserung bieten. Wird das machbar sein? Sie könnten in ein paar Sekunden Ergebnisse sehen.

mysql -e 'select search.* from search join contains using (keyword)' > outfile.txt 

# but first you need to create the tables like this (only once off) 

create table contains (
    keyword varchar(255) 
    , primary key (keyword) 
); 

create table search (
    keyword varchar(255) 
    ,num bigint 
    ,key (keyword) 
); 

# and load the data in: 

load data infile 'file.contain.query.txt' 
    into table contains fields terminated by "add column separator here"; 
load data infile 'file.to.search.in.txt' 
    into table search fields terminated by "add column separator here"; 
+0

Ich habe dies nicht getestet, aber es wird mit ein paar Optimierungen funktionieren, abhängig von Ihrer Situation. Es wird sehr wenig Speicher benötigen, es sei denn, Sie möchten, dass es rambasiert ist. –

0
use strict; 
use warings; 

system("sort file.contain.query.txt > qsorted.txt"); 
system("sort file.to.search.in.txt > dsorted.txt"); 

open (QFILE, "<qsorted.txt") or die(); 
open (DFILE, "<dsorted.txt") or die(); 


while (my $qline = <QFILE>) { 
    my ($queryid) = ($qline =~ /ENST(\d+)/); 
    while (my $dline = <DFILE>) { 
    my ($dataid) = ($dline =~ /ENST(\d+)/); 
    if ($dataid == $queryid) { print $qline; } 
    elsif ($dataid > $queryid) { break; } 
    } 
} 
6

Wenn Sie Strings festgelegt haben, grep -F -f verwenden. Dies ist wesentlich schneller als die Regex-Suche.

5

Wenn die Dateien bereits sortiert:

join file1 file2 

wenn nicht:

join <(sort file1) <(sort file2) 
4

Wenn Sie Perl-Version 5.10 oder höher verwenden, können Sie die 'query' Begriffe in einen regulären Ausdruck kommen mit den Abfragebegriffen getrennt durch die 'Pipe'. (Like: ENST001|ENST002|ENST003) Perl erstellt einen "Trie", der wie ein Hash-Lookup in konstanter Zeit sucht. Es sollte so schnell wie die Lösung mit einem Nachschlage-Hash laufen. Nur um einen anderen Weg zu zeigen, dies zu tun.

#!/usr/bin/perl 
use strict; 
use warnings; 
use Inline::Files; 

my $query = join "|", map {chomp; $_} <QUERY>; 

while (<RAW>) { 
    print if /^(?:$query)\s/; 
} 

__QUERY__ 
ENST001 
ENST002 
ENST003 
__RAW__ 
ENST001 90 
ENST002 80 
ENST004 50