Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Perl: Системное программирование > Поиск и подсчет строк в большом файле


Автор: Zynchak 14.12.2007, 16:39
Доброго дня всем!

У меня возникла проблема, а так как я только учусь писать на Перл, то и загвоздка серезная для меня.

Вот суть задачи:
Есть большой файл, около 30МБ. Требуется найти и подсчитать в нем все строки.
К примеру, берем первую строку и ищем сколько дублей во всем файле, результат записываем в файл в виде:

строка;количество

Дале вторую строку и такие же операции,  и так до конца файла, при том я вот незнаю как по несколько раз не считать одну и ту же строку, ведь мы ее могли уже считать.

Помогите мне пожалуста, очень нужно!

Заранее спасибо!

Автор: amg 14.12.2007, 17:06
Посмотри пока на этот код. Если не устроит, будем думать ( требуется много памяти и порядок строк произвольный будет).
Код

while (<FILE>) {
  chomp;
  $h{$_}++;
}
foreach (keys %h) {
  print "$_;$h{$_}\n";
}

Автор: Zynchak 19.12.2007, 09:28
У меня почему то ругается -
Global symbol "%h" requires explicit package name at search.pl line...

Добавлено через 2 минуты и 15 секунд
В принципе можно упростить задачу, сначало сделать еще один файл гдк будут только уникальные записи и уже от туда скчитывать кей а в первичном файле вести поиск.

Автор: amg 19.12.2007, 10:43
Цитата(Zynchak @  19.12.2007,  09:28 Найти цитируемый пост)
У меня почему то ругается -Global symbol "%h" requires explicit package name at search.pl line...
Это из-за use strict; Добавь в начале my %h;

Автор: Ramirez 19.12.2007, 11:33
Если строки достаточно длинные (10-15 символов и более), то возможно будет эффективнее из строки сразу считать md5-хеш, и сравнивать и хранить в массиве уже их. Памяти будет требоваться меньше, но чуть больше будет нагружен процессор (вычисляя хеши) , но думаю что нагрузка будет несущественна.

Автор: amg 19.12.2007, 12:14
Проверил вот такой код, печатающий только уникальные строки, сохраняющий порядок строк исходного файла и добавляющий в конец каждой строки номера строк исходного файла.
Код

while (<FILE>) {
  chomp;
  push @{$h{$_}}, $.;
}
foreach (sort {$h{$a}[0]<=>$h{$b}[0]} keys %h) {
  print "$_;@{$h{$_}}\n";
}
В принципе, ничего страшного. Для 30 Мб файла (450 тысяч строк, все уникальные) требуется 150 Мб памяти и 15 с (сортировка много времени занимает). Если бы было много одинаковых строк, было бы быстрее (и памяти меньше).

Автор: Zynchak 19.12.2007, 14:25
Вроде все получилось, вот код (search.pl):

#!/usr/bin/perl -w

use strict;
use warnings;

my %h;
my $path =$ARGV[0];
open( FILE, "< $path" ) or die $!;

while (<FILE>) {
  chomp;
  $h{$_}++;
}
foreach (keys %h) {
  print "$_;$h{$_}\n";
}


Запускаю вот так:

perl search.pl key.txt >> base.txt

В файле base.txt получаю вот такой результат, вроде все правильно работает:

keyword-a;65
keyword-b;2
keyword-c;7


Автор: amg 19.12.2007, 14:48
Еще так можно:
Код

my (%h, @a);
while (<FILE>) {
  chomp;
  $h{$_}++ or push @a, $_;
}
foreach (@a) {
  print "$_;$h{$_}\n";
}
Займет немного больше памяти, но зато порядок строк будет такой же, как в исходном файле (если это важно). 
Этот вариант работает намного быстрее, чем мой предыдущий пример (который с сортировкой), да и памяти потребляет меньше.

Добавлено через 12 минут и 15 секунд
Цитата(Zynchak @  19.12.2007,  14:25 Найти цитируемый пост)
Запускаю вот так:
Кстати, именно для случаев такого запуска в перле имеется оператор <>. С ним файлы из аргументов сами откроются, просто
Код

while (<>) {
   ...
}


Автор: OutlawZ 28.4.2011, 02:23
Если придется работать с тремя файлами одновременно ? то как подсчитать количество совпавших строк? что бы к примеру программа выводила "Эта строка повторяется n-количество раз в файлах: file.txt, file2.txt" буду признателен за ответ.

Автор: alezzz 30.4.2011, 18:09
усложните хеш, например:
Код

$h{$_}{$filename}++;


Автор: OutlawZ 1.5.2011, 00:50
Можно пример? а то у меня каша получается в плане том что счетчик совпадений перестал работать и дописывается просто к строке, что не есть хорошо, может сделать хеш хешей????

Автор: SkoobyDoo 1.5.2011, 08:08
Код

#!/usr/bin/env perl

use strict;
use warnings;
use IO::File;

my (%h);

my $fh = new IO::File;
for my $f (@ARGV) {
  $fh->open("< $f");

  while (<$fh>) {
    chomp;
    s/^\s+//;
    s/\s+$//;
    next unless /[\w]+/;
    $h{$f}{$_}++;
  }

}
$fh->close;

for my $k (keys %h) {
  print $k, "\n";
  map { print "  ", $h{$k}{$_}, ": ", $_, "\n" } keys %{$h{$k}};
}

Автор: alezzz 1.5.2011, 09:58
Вот еще вариант, тут должно меньше памяти расходоваться на хэш:
Код

@files = @ARGV;

foreach $i (0..$#files){
    open $f, "<$files[$i]";
    while(<$f>){
        chomp;
        $lines{"$_"}[$i]++ if $_ !~ /^\s*$/;
    }
    close $f;
}

foreach $line (keys %lines){
    my $count;
    print "$line:\n";
    foreach $i (0..$#files){
        print "    $files[$i] - $lines{$line}[$i]\n" if $lines{$line}[$i];
        $count += $lines{$line}[$i];
    } 
    print"    all - $count\n";
}

Автор: OutlawZ 2.5.2011, 20:28
Всем спасибо, протестировал, доволен результатом.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)