2012-03-22 68 views
1

我試圖重新回到Perl,並有我的代碼時間的bug bug聲。我有一個很大的源.DAT文件(2GB)。我有另一個.TXT文件,其中包含字符串(近2000個),我想在該.DAT文件中搜索。我將該.TXT文件中的值放入數組中。什麼是一個有效的方式來搜索一個巨大的文件的許多字符串?

我想高效地搜索數組中的每個字符串並輸出匹配。任何人都可以幫我理清我嗎?提前致謝!

my $source = "/KEYS.txt"; 
my $data= "/claims.dat"; 
my @array; 
my $arraySize = scalar (@DESYarray); 

open (DAT, $data) or die "Cannot open file!"; 
open (LOG, ">>/output.log"); 

open (TXT,$source); 
while (my $searchValues = <TXT>) { 
    push (@array, $searchValues); 
} 
close (TXT); 


while (my $line = <DAT>) {  
for (my $x = 0; $x <= $arraySize; $x++) { 
    if (my $line =~ /$array[$x]/) { 
     print LOG $line; 
    } 
} 
} 

close (DAT); 
close (LOG); 
+2

對於這個特定的問題,您可以可能使用'grep'(在Linux/Unix版本,不使用Perl)。例如。 'grep -f /KEYS.txt/claims.dat'。 – TLP 2012-03-22 19:02:39

+0

KEYS.txt中的字符串是固定的還是他們的正則表達式?如果它們是固定的,用'grep -F -f KEYS.txt claims.dat'獲得巨大的速度勝利; Perl代碼將使用['index'](http://p3rl.org/index)函數而不是匹配運算符。 – daxim 2012-03-22 19:12:36

+0

在KEYS.txt中,它們實際上是9位數字。所以102361550和481543095等 – cluckinchicken 2012-03-22 19:45:40

回答

1

您重新聲明my $line在你的內循環,這意味着它將等於:

if (undef =~ /$array[$x]/) { 

這當然總是會失敗。如果您用過use warnings,你會得到錯誤:

Use of uninitialized value in pattern match (m//) at ... 

這讓我沒有使用警告懷疑,這是一個非常壞主意。

另外,請記住,當您將值讀入@array時,您將在最後得到一個換行符,因此您在DAT文件中搜索以\n結尾的字符串,這可能不是您想要的。例如。如果您有foo\n,則它不會匹配foo bar baz

該解決方案是chomp您的數據:

chomp(my @array = <TXT>); 

是的,你可以的Chomp的數組,你可以指定整個文件到一個數組這種方式。

你可以也應該改進你的腳本。除非實際上需要使用索引來進行某些操作,否則使用數組索引進行循環是非常不必要的。

use strict; 
use warnings; # ALWAYS use these! 
use autodie;  # handles the open statements for convenience 

my $source = "/KEYS.txt"; 
my $data= "/claims.dat"; 

open $txt, '<', $source; 
chomp(my @array = <$txt>); 
close $txt; 

open my $dat, '<', $data; # use three argument open and lexical file handle 
open my $log, '>>', "/output.log"; 

while (<$dat>) {   # using $_ for convenience 
    for my $word (@array) { 
     if (/\Q$word/i) { # adding /i modifier to match case insensitively 
      print $log $line; # also adding \Q to match literal strings 
    } 
} 

使用\Q可能是非常重要的,這取決於你的KEYS.txt文件包含。正則表達式的元字符可能會導致細微的不匹配,如果您期望它們字面匹配。例如。如果你有一個詞,如foo?,正則表達式/foo?/將匹配foo,但它也將匹配for

此外,您可能希望決定是否允許部分匹配。例如。 /foo/也將匹配football。爲了克服這種情況,一種方法是使用單詞邊界轉義字符:

/\b\Q$word\E\b/i 

你需要將它們放在\Q .. \E序列之外,否則將被字面解釋。正如tchrist指出的那樣,Borodin建議,用所有單詞構建一個正則表達式可以節省您重複的行數。例如。如果您的文字爲"foo","bar""baz",並且行foo bar baz您會得到這行打印三次,每個匹配的單詞一次。

之後可以通過以某種合適的方式扣除數據來解決此問題。只有你知道你的數據以及這是否是一個問題。由於性能原因,我會毫不猶豫地編譯這麼長的正則表達式,但您可以嘗試一下,看看它是否適合您。

+1

你應該預編譯你的正則表達式。另外,爲什麼要爲每場比賽打印一條線?只需打印任何匹配的行。這導致了一個'... | ... | ... | ...'解決方案,這個解決方案將被優化爲一個trie數據結構。 – tchrist 2012-03-22 19:06:44

+0

@tchrist因​​爲我不確定構建一個2000字的正則表達式是最優的。也許perl可以優化它,這是一個好主意。 – TLP 2012-03-22 19:12:16

+0

@tchrist通過預編譯,我假設你的意思就像'@array = map qr/\ Q $ _/i,@ array'?這會提高性能嗎? – TLP 2012-03-22 19:25:03

1

您應該始終使用use strictuse warnings開始您的程序,特別是在您要求獲得代碼幫助時。他們對調試有很大幫助,並且經常會發現容易被忽略的簡單錯誤。

KEYS.txt中的字符串有多長?使用join '|', @array從他們構建正則表達式可能是可行的。順便說一句,你寫的代碼相當於@array = <TXT>,不要忘記chomp的內容!

我建議這樣的事情

use strict; 
use warnings; 

my $source = "/KEYS.txt"; 
my $data= "/claims.dat"; 

open my $dat, '<', $data or die "Cannot open data file: $!"; 
open my $log, '>>', '/output.log' or die "Cannot open output file: $!"; 

open my $txt, '<', $source or die "Cannot open keys file: $!"; 
my @keys = <$txt>; 
chomp @keys; 
close $txt; 

my $regex = join '|', map quotemeta, @keys; 
$regex = qr/$regex/i; 

while (my $line = <$dat>) { 
    next unless $line =~ $regex; 
    print $log $line; 
} 

close $log or die "Unable to close log file: $!"; 
0
我用正則表達式

::過去組裝拿令牌的列表,創建一個優化的正則表達式,並用它篩選大量的文字。一旦我們從|移除用Regexp :: Assemble分隔正則表達式,我們看到了很大的性能提升。

Regexp::Assemble

+2

現在會自動發生。 http://p3rl.org/perl5100delta#Trie-optimisation-of-literal-string-alternations – daxim 2012-03-22 19:23:26

相關問題