0

我在性能方面面临很大问题。我必须在包含大约 600 个子目录的巨大目录(30 gb)中的文件中搜索关键字(其中将再次包含许多子目录)。

目前我将子目录拆分为 50 个文本文件,因此每个文件将获得 12 个子目录名称并运行所有 50 个进程并行。

my $pm = Parallel::ForkManager->new($lines);

# Forks and returns the pid for the child:
my $pid = $pm->start and next;

# we are now in the child process
ucm5 ("-iinput.txt","-f$data"); - here $data will be text file names(text1,text2...text50)
--input.txt will have the multiple search keywords(hi , hello)

$pm->finish; # Terminates the child process

#!/usr/bin/perl
sub ucm5 {
local @ARGV = @_;
use strict;
use warnings;  
use File::Find;
use Getopt::Std;
#getting the input parameters
getopts('i:f:');

our($opt_i, $opt_f);
my $searchKeyword = $opt_i;                               #Search keyword file.
my $intfSplit = $opt_f;                               #split file
my $path = "C:/";                           #source directory
my $searchString;                                   #search keyword

open FH, ">>log.txt";                                          #open the log file to write

print FH "$intfSplit ". "started at ".(localtime)."\n";       #write the log file

open (FILE,$intfSplit);                                       #open the split file to read

while(<FILE>){

   my $intf= $_;                                              #setting the interface to intf
   chomp($intf);
   my $dir = $path.$intf;
   chomp($dir);
   print "$dir \n";                                              
   open(INP,$searchKeyword);                                  #open the search keyword file to read

   while (<INP>){      

   $searchString =$_;                                         #setting the search keyword to string
   chomp($searchString);
   print "$searchString \n";
   #open my $out, ">", "vob$intfSplit.txt" or die $!;          #open the vobintfSplit_* file to write
   open my $out, ">", "vob$intfSplit.txt" or die $!;
#calling subroutine printFile to find and print the path of element


#the subroutine will search for the keyword and print the path if keyword is exist in file.
my $printFile = sub {
   my $element = $_;

   if(-f $element && $element =~ /\.*$/){ 

      open my $in, "<", $element or die $!;
      while(<$in>) {
         if (/\Q$searchString\E/) {
            my $last_update_time = (stat($element))[9];
            my $timestamp  = localtime($last_update_time);
            print $out "$File::Find::name". "     $element"."     $timestamp". "     $searchString\n";
            last;
          }
        }
      }
    };
find(\&$printFile,$dir);  
  }
}
print FH "$intfSplit ". "ended at ".(localtime)."\n";         #write the log file
}
1;

代码可能有点混乱,我将解释它在做什么 - 在第一个 while 循环中,它打开包含子目录的文本文件,然后在另一个 while 循环中打开包含搜索词 (hi,hello) 的文本文件。在该 file::find 中将被调用以搜索子目录中的关键字。

现在发生的事情是它进入第一个子目录并搜索第一个关键字(HI),一旦完成,它就会再次进入同一个目录并搜索下一个关键字(Hello),这意味着读取同一个目录两次。

但我想在第一次阅读时搜索这两个关键字,这将节省大量时间。我的输出应该有路径、文件名、搜索字。

例子

C:/aims/if/sp/abcd.sql abcd.sql 你好

C:/aims/if/sp/abcd.sql abcd.sql 你好

请在这个问题上帮助我。除了并行处理和线程之外,还有其他更好的方法来搜索具有多个关键字的所有 600 个子目录吗?

4

1 回答 1

0

您可能只想一次性匹配所有关键字,因此只需扫描文件一次。

下面的代码将查找“hello”或“test”并在输入中打印任何匹配的行。如果您想在关键字上使用 quotemeta() 以防止将正则表达式模式嵌入到您的单词中,可能值得考虑

while (<>) {
    if (/\b(hello|test)\b/i) { print "$1: $_" }
}

\b 匹配单词边界(因此它不会匹配“testing”)并且 /i 使整个测试用例不敏感。匹配的单词以 $1 结尾。


编辑:更完整的示例显示如何处理多个匹配项。

假设我们有一个示例文件 /tmp/test.txt 和一个 perl 脚本 match.pl(在我的示例中它只处理一个文件)。

测试数据:

This is a test
Line two
And this is line three

剧本:

#!perl
use v5.10;
use warnings;
use File::Slurp;
my $contents = read_file('/tmp/test.txt');

my @raw_matches = $contents =~ /(test|line)/gi;

my %match_counts;
foreach (@raw_matches) {
    $match_counts{ lc($_) }++;
}

my @unique_matches = sort keys %match_counts;
foreach (@unique_matches) {
    say "$_ : count = $match_counts{$_}";
}

样本输出:

line : count = 2
test : count = 1

现在,这确实依赖于能够将整个文件读入脚本中的内存,但是您没有提到任何大文件,只是需要处理很多文件。上面的例子可能会用更短的方式来表达,但希望每个部分都在做什么很清楚。

将上述内容转换为每个文件可以调用一次的函数应该足够简单。

于 2013-02-04T10:08:16.067 回答