Есть ли лучший способ "grep" из большого файла, чем использование `grep` в perl? - PullRequest
1 голос
/ 23 марта 2011

$rvsfile - это путь к файлу около 200M. Я хочу посчитать номер строки, в которой есть $userid. Но использование grep в цикле while кажется очень медленным. Так есть ли эффективный способ сделать это? Поскольку $rvsfile очень большой, я не могу прочитать его в память, используя @tmp = <FILEHANDLE>.

while(defined($line = <SRCFILE>))
{
    $line =~ /^([^\t]*)\t/;
    $userid = $1;
    $linenum = `grep '^$userid\$' $rvsfile | wc -l`;
    chomp($linenum);
    print "$userid $linenum\n";
    if($linenum == 0)
    {
        print TARGETFILE "$line";
    }
}

А как мне получить деталь до \t в строке без regex? Например, строка может выглядеть так:

2013123 \t что-то

Как я могу получить 2013123 без регулярных выражений?

Ответы [ 6 ]

3 голосов
/ 23 марта 2011

Да, вы разветвляете оболочку при каждом вызове цикла. Это медленно. Вы также прочитали весь $rsvfile один раз для каждого пользователя. Это слишком много работы.

  1. Прочитайте SRCFILE один раз и создайте список @userids.
  2. Считайте $rvsfile один раз, сохраняя текущий счетчик каждого идентификатора пользователя.

Эскиз:

my @userids;

while(<SRCFILE>)
{
    push @userids, $1 if /^([^\t]*)\t/;
}

my $regex = join '|', @userids;
my %count;

while (<RSVFILE>)
{
     ++$count{$1} if /^($regex)$/o
}

# %count has everything you need...
1 голос
/ 23 марта 2011

Использовать хэши:

my %count;
while (<LARGEFILE>) {
    chomp;
    $count{$_}++;
};
# now $count{userid} is the number of occurances 
# of $userid in LARGEFILE

Или, если вы боитесь использовать слишком много памяти для хэша (т. Е. Вас интересует 6 пользователей, а в большом файле их больше 100 КБ), сделайте это по-другому:

my %count;
while (<SMALLFILE>) {
    /^(.*?)\t/ and $count{$_} = 0;
};

while (<LARGEFILE>) {
    chomp;
    $count{$_}++ if defined $count{$_};
};
# now $count{userid} is the number of occurances 
# of $userid in LARGEFILE, *if* userid is in SMALLFILE
1 голос
/ 23 марта 2011

Если <SRCFILE> относительно мало, вы можете сделать это наоборот.Читайте в файле большего размера по одной строке за раз и проверяйте каждый идентификатор пользователя на строку, сохраняя счет каждого идентификатора пользователя, используя структуру хэширования.Что-то вроде:

my %userids = map {($_, 0)}                # use as hash key with init value of 0
              grep {$_}                    # only return mataches
              map {/^([^\t]+)/} <SRCFILE>; # extract ID

while (defined($line = <LARGEFILE>)) {
    for (keys %userids) {
        ++$userids{$_} if $line =~ /\Q$_\E/; # \Q...\E escapes special chars in $_
    }
}

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

1 голос
/ 23 марта 2011

Если я правильно прочитал, вы хотите что-то вроде этого:

#!/usr/bin/perl

use strict;
use warnings;

my $userid = 1246;
my $count = 0;

my $rsvfile = 'sample';

open my $fh, '<', $rsvfile;

while(<$fh>) {
  $count++ if /$userid/;
}

print "$count\n";

или даже, (и кто-то исправит меня, если я ошибаюсь, но это не значит, что все файлы читаются целиком)

#!/usr/bin/perl

use strict;
use warnings;

my $userid = 1246;

my $rsvfile = 'sample';

open my $fh, '<', $rsvfile;

my $count = grep {/$userid/} <$fh>;

print "$count\n";
1 голос
/ 23 марта 2011

Вы можете искать местоположение первого \ t, используя index , что будет быстрее.Затем вы можете использовать сплайс , чтобы получить совпадение.

Предложить вам тест различные подходы.

0 голосов
/ 23 марта 2011

если у вас есть выбор, попробуйте его с помощью awk

awk 'FNR==NR{a[$1];next} { for(i in a) { if ($0 ~ i) { print $0} } } ' $SRCFILE $rsvfile
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...