Как можно объединить строки в большой несортированный файл без нехватки памяти в Perl? - PullRequest
3 голосов
/ 13 февраля 2009

У меня есть очень большой файл с разделителями столбцов, выходящий из отчета базы данных примерно так:

field1,field2,field3,metricA,value1
field1,field2,field3,metricB,value2

Я хочу, чтобы в новом файле были строки комбинирования, подобные этой:

field1,field2,field3,value1,value2

Я могу сделать это, используя хэш. В этом примере первые три поля являются ключом, и я объединяю value1 и value в определенном порядке, чтобы быть значением. После прочтения файла я просто распечатываю ключи и значения хеш-таблицы в другом файле. Работает нормально.

Однако у меня есть некоторые опасения, так как мой файл будет очень большим. Около 8 ГБ на файл.

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

Для полного раскрытия, я использую ActiveState Perl в Windows.

Ответы [ 5 ]

6 голосов
/ 13 февраля 2009

Если ваши строки отсортированы по ключу или по какой-то другой причине равные значения field1, field2, field3 являются смежными, то конечный автомат будет намного быстрее. Просто прочитайте строки и, если поля такие же, как в предыдущей строке, выведите оба значения.

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

5 голосов
/ 14 февраля 2009

Если вы не думаете, что данные поместятся в память, вы всегда можете связать Ваш хэш к базе данных на диске:

use BerkeleyDB;
tie my %data, 'BerkeleyDB::Hash', -Filename => 'data';

while(my $line = <>){
    chomp $line;
    my @columns = split /,/, $line; # or use Text::CSV_XS to parse this correctly

    my $key = join ',', @columns[0..2];
    my $a_key = "$key:metric_a";
    my $b_key = "$key:metric_b";

    if($columns[3] eq 'A'){
        $data{$a_key} = $columns[4];
    }
    elsif($columns[3] eq 'B'){
        $data{$b_key} = $columns[4];
    }

    if(exists $data{$a_key} && exists $data{$b_key}){
        my ($a, $b) = map { $data{$_} } ($a_key, $b_key);
        print "$key,$a,$b\n";
        # optionally delete the data here, if you don't plan to reuse the database
    }
}
5 голосов
/ 13 февраля 2009

Если у вас были другие инструменты, подобные Unix (например, через cygwin), вы могли бы предварительно отсортировать файл, используя команду sort (которая может справиться с огромными файлами). Или, возможно, вы можете получить базу данных для вывода отсортированного формата.

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

3 голосов
/ 13 февраля 2009

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

2 голосов
/ 14 февраля 2009

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

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