Как отсортировать строки в текстовом файле в Perl? - PullRequest
3 голосов
/ 27 августа 2010

У меня есть пара текстовых файлов (A.txt и B.txt), которые выглядят так (может иметь ~ 10000 строк в каждом)

processa,id1=123,id2=5321
processa,id1=432,id2=3721
processa,id1=3,id2=521
processb,id1=9822,id2=521
processa,id1=213,id2=1
processc,id1=822,id2=521

Мне нужно проверить, присутствует ли каждая строка в файле A.txt и в B.txt (B.txt тоже может иметь больше, это нормально).

Дело в том, что строки могут быть в любом порядке в двух файлах, поэтому я думаю, что отсортирую их в каком-то определенном порядке в обоих файлах в O(nlogn), а затем сопоставлю каждую строку в A.txt со следующим строки в B.txt в O(n). Я мог бы реализовать хеш, но файлы велики, и это сравнение происходит только один раз, после чего эти файлы регенерируются, поэтому я не думаю, что это хорошая идея.

Каков наилучший способ сортировки файлов в Perl? Любое упорядочение подойдет, просто нужно некоторое упорядочение.

Например, при упорядочении словаря это будет

processa,id1=123,id2=5321
processa,id1=213,id2=1
processa,id1=3,id2=521
processa,id1=432,id2=3721
processb,id1=9822,id2=521
processc,id1=822,id2=521

Как я упоминал ранее, любой порядок будет таким же хорошим, если Perl быстр в этом.

Я хочу сделать это из кода Perl, после открытия файла вот так

open (FH, "<A.txt");

Любые комментарии, идеи и т. Д. Будут полезны.

Ответы [ 6 ]

5 голосов
/ 27 августа 2010

Чтобы отсортировать файл в вашем скрипте, вам все равно придется загрузить все это в память.Если вы делаете это, я не уверен, в чем преимущество сортировки по сравнению с простой загрузкой в ​​хеш?

Примерно так будет работать:

my %seen;
open(A, "<A.txt") or die "Can't read A: $!";
while (<A>) {
    $seen{$_}=1;
}
close A;

open(B, "<B.txt") or die "Can't read B: $!";
while(<B>) {
  delete $seen{$_};
}
close B;

print "Lines found in A, missing in B:\n";
join "\n", keys %seen;
1 голос
/ 28 августа 2010

Вот еще один способ сделать это. Идея состоит в том, чтобы создать гибкую структуру данных, которая позволяет легко отвечать на многие вопросы с помощью grep.

use strict;
use warnings;

my ($fileA, $fileB) = @ARGV;

# Load all lines: $h{LINE}{FILE_NAME} = TALLY
my %h;
$h{$_}{$ARGV} ++ while <>;

# Do whatever you need.
my @all_lines = keys %h;
my @in_both   = grep {     keys %{$h{$_}} == 2       } keys %h;
my @in_A      = grep {     exists $h{$_}{$fileA}     } keys %h;
my @only_in_A = grep { not exists $h{$_}{$fileB}     } @in_A;
my @in_A_mult = grep {            $h{$_}{$fileA} > 1 } @in_A;
0 голосов
/ 28 августа 2010

Как обычно, CPAN имеет ответ на этот вопрос. Sort :: External или File :: Sort выглядит так, как будто это будет работать.У меня никогда не было повода попробовать, поэтому я не знаю, что было бы лучше для вас.

Другой возможностью было бы использование AnyDBM_File для создания хэша на основе диска,может превышать доступную память.Без этого я не мог бы сказать, будет ли использование файла DBM быстрее или медленнее, чем сортировка, но код, вероятно, будет проще.

0 голосов
/ 28 августа 2010

Проверка, является ли A.txt подмножеством B.txt

open FILE.B, "B.txt";
open FILE.A, "A.txt";

my %bFile;

while(<FILE.B>) {
   ($process, $id1, $id2) = split /,/;
   $bFile{$process}{$id1}{$id2}++;
}

$missingRows = 0;

while(<FILE.A>) {
   $missingRows++ unless $bFile{$process}{$id1}{$id2};
   # If we've seen a given entry already don't add it
   next if $missingRows; # One miss means they aren't all verified
}

$is_Atxt_Subset_Btxt = $missingRows?FALSE:TRUE;

Это даст вам тест для всех строк в A, находящихся в B, с чтением только во всех B, а затем проверкой каждого члена массива во время чтения A.

0 голосов
/ 27 августа 2010

Могу я спросить, почему вы должны делать это на родном Perl?Если стоимость вызова системного вызова или 3 не является проблемой (например, вы делаете это нечасто и не в тесном цикле), почему бы просто не сделать:

my $cmd = "sort $file1 > $file1.sorted";
$cmd .= "; sort $file2 > $file2.sorted";
$cmd .= "; comm -23 $file1.sorted $file2.sorted |wc -l";
my $count = `$cmd`;
$count =~ s/\s+//g;
if ($count != 0) {
    print "Stuff in A exists that aren't in B\n";
}

Обратите внимание, что параметр comm можетбыть разными, в зависимости от того, что именно вы хотите.

0 голосов
/ 27 августа 2010

Хорошо, я обычно анализирую очень большие (600 МБ) файлы ежедневного журнала Apache с помощью Perl, и для хранения информации использую хэш.Я также просматриваю около 30 из этих файлов в одном экземпляре сценария, используя тот же хэш.это не страшно, если у вас достаточно оперативной памяти.

...