Какой эффективный способ поиска огромного файла по множеству строк символов? - PullRequest
1 голос
/ 22 марта 2012

Я пытаюсь вернуться в Perl и у меня много времени с моим кодом.У меня большой исходный файл .DAT (2 ГБ).У меня есть другой файл .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);

Ответы [ 3 ]

1 голос
/ 22 марта 2012

Вы всегда должны запускать свою программу с use strict и use warnings, особенно если вы просите помощи с вашим кодом. Они очень помогают при отладке и часто находят простые ошибки, которые в противном случае легко упускаются из виду.

Как долго строки в KEYS.txt ? Может быть целесообразно построить регулярное выражение из них, используя join '|', @array. Кстати, код, который вы написали, эквивалентен @array = <TXT>, и не забудьте сжать содержимое!

Я предлагаю что-то вроде этого

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: $!";
1 голос
/ 22 марта 2012

Вы повторно объявляете 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>);

Да, вы можете сжать массив, и вы можете назначить весь файл массиву таким образом.

Вы можете и должны немного улучшить свой скрипт.Совершенно не нужно зацикливаться с использованием индексов массива, если только вам фактически не нужно использовать индексы для чего-либо.

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, иначе они будут интерпретироваться буквально.

ETA: Как указывает Трист и предлагает Бородин, построение регулярного выражения со всеми словами спасет вас от получения повторяющихся строк.Например, если у вас есть слова "foo", "bar" и "baz" и строка foo bar baz, вы напечатаете эту строку три раза, по одному разу для каждого соответствующего слова.

Это может быть исправлено впоследствии путем дедупликации ваших данных подходящим способом.Только вы знаете свои данные, и является ли это проблемой или нет.Из-за производительности я бы скомпилировал такое длинное регулярное выражение, но вы можете попробовать и посмотреть, работает ли он для вас.

0 голосов
/ 22 марта 2012

В прошлом я использовал Regexp :: Assemble, чтобы получить список токенов, создать оптимизированное регулярное выражение и использовать его для фильтрации большого количества текста. Как только мы переехали из | разделив regexp на Regexp :: Assemble, мы увидели значительное повышение производительности.

Regexp :: Собрать

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...