Perl сортировать вопрос - PullRequest
3 голосов
/ 03 июня 2011

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

bla bla bla  0x97860afa bla bla  

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

 bla bla bla  0x97860afc bla bla  
     bla bla {  
         blabla  
            bla bla {  
                bla     
            }  
        }  

Я, наверное, могу понять это, но, возможно, есть простое решение на perl или awk, которое сэкономит мне 1/2 дня.


Передача комментариев из OP:

Отступы могут быть пробелом или табуляцией, я могу улучшить это в любом предложенном решении, я думаю, что Брайан суммирует хорошо: В частности, вы хотите отсортировать «элементы», которые определены каккусок текста, который начинается со строки, содержащей «0xNNNNNNNN», и содержит все, вплоть до (но не включая) следующую строку, которая содержит «0xNNNNNNNN» (где, конечно, меняется N) .Без вкраплений строк.

Ответы [ 5 ]

2 голосов
/ 03 июня 2011

Если файлы не слишком велики для памяти, я бы выбрал решение TLP.Если они есть, вы можете немного изменить его и распечатать в файл, как он предлагает.Добавьте это перед while (все непроверенные, ymmv, caveat programmer и т. Д.):

my $currentInFile        = "";
my $currentOutFileHandle = "";

и измените тело while с текущего if-else на

if ($currentInFile ne $ARG) {
    if (fileno($currentOutFileHandle)) {
        if (!close($currentOutFileHandle)) {
            # whatever you want to do if you can't close the previous output file
        }
    }
    my $newOutFile = $ARG . ".tagged";
    if (!open($currentOutFileHandle, ">", $newOutFile)) {
        # whatever you want to do if you can't open a new output file for writing
    }
}

if (...conditional from TLP...) {
    # add more zeroes if the files really are that large :)
    $lastkey = $1 . " " . sprintf("%0.10d", $.);
}

if (fileno($currentOutFileHandle)) {
    print $currentOutFileHandle $lastkey . "\t" . $line;
}
else {
    # whatever you want to do if $currentOutFileHandle's gone screwy
}

Теперь вы будете иметь тег foo.log.tagged для каждого файла foo.log, который вы его кормили;файл .tagged содержит в точности содержимое оригинала, но к каждой строке добавляется «0xNNNNNNNN LLLLLLLLLL \ t» (LLLLLLLLLL -> номер строки с нулевым отступом).sort(1) на самом деле неплохо справляется с обработкой больших данных, хотя вы захотите взглянуть на аргумент --tevent-directory, если вы думаете, что он переполнит / tmp своими временными файлами, пока просматривает материал, который вы передаете.Что-то вроде этого должно помочь вам начать:

sort --output=/my/new/really.big.file --temporary-directory=/scratch/dir/on/roomy/partition *.tagged

Затем обрежьте теги, если хотите:

perl -pi -e 's/^[^\t]+\t//' /my/new/really.big.file

FWIW, я добавил номера строк, чтобы не беспокоиться о таких вещахсортировка строки 10 перед строкой 2, если их шестнадцатеричные ключи были идентичны - поскольку шестнадцатеричные числа являются основным критерием сортировки, мы не можем просто выполнить числовую сортировку.

2 голосов
/ 03 июня 2011

Примерно так может работать (не проверено):

my $line;
my $lastkey;
my %data;
while($line = <>) {
    chomp $line;
    if ($line =~ /\b(0x\p{AHex}{8})\b/) {
        # Begin a new entry
        my $unique_key = $1 . $.; # cred to [Brian Gerard][1] for uniqueness
        $data{$1} = $line;
        $lastkey = $unique_key;
    } else {
        # Continue an old entry
        $data{$lastkey} .= $line;
    }
}

print $data{$_}, "\n" for (sort { $a <=> $b } keys %data);

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

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

2 голосов
/ 03 июня 2011
  • Для Огромных файлов данных, я бы порекомендовал Sort::External.
  • Не похоже, что вам нужно разбирать скобки, если отступ делает свою работу.Затем вы должны сделать это на «перерывах», или когда уровень отступа 0, то вы обрабатываете последнюю собранную запись, поэтому вы всегда смотрите вперед на одну строку.

Итак:

sub to_sort_form {
    my $buffer = $_[0];
    my ( $id ) = $buffer =~ m/(0x\p{AHex}{8})/; # grab the first candidate
    return "$id-:-$buffer";
    $_[0] = '';
}

sub to_source { 
    my $val = shift;
    my ( $record ) = $val =~ m/-:-(.*)/;
    $record =~ s/\$--\^/\n/g;
    return $record;
}

my $sortex = Sort::External->new(
      mem_threshold   => 1024**2 * 16     # default: 1024**2 * 8 (8 MiB)
    , cache_size      => 100_000          # default: undef (disabled) 
    , sortsub         => sub { $Sort::External::a cmp $Sort::External::b }
    , working_dir     => $temp_directory  # default: see below
);

my $id;
my $buffer = <>;
chomp $buffer;
while ( <> ) { 
    my ( $indent ) = m/^(\s*)\S/;
    unless ( length $indent ) {
        $sortex->feed( to_sort_form( $buffer ));
    }
    chomp;
    $buffer .= $_ . '$--^';
}
$sortex->feed( to_sort_form( $buffer ));
$sortex->finish;

while ( defined( $_ = $sortex->fetch ) ) {
    print to_source( $_ );
}

Допущения:

  • Строка '$--^' не отображается в данных сама по себе.
  • То, что вас не пугает около двух8 шестнадцатеричных строк в одной записи.
0 голосов
/ 04 июня 2011

Решение от TLP работало хорошо с некоторыми незначительными изменениями. Хорошей идеей было добавить все в одну строку перед сортировкой, затем я должен добавить разбор позиции для восстановления свернутых блоков кода, но это легко. ниже - окончательная протестированная версия. Спасибо всем, stackoverflow потрясающий.

#!/usr/bin/perl -w
my $line;
my $lastkey;
my %data;
while($line = <>) {
  chomp $line;
  if ($line =~ /\b(0x\p{AHex}{8})\b/) {
    # Begin a new entry
    #my $unique_key = $1 . $.; # cred to [Brian Gerard][1] for uniqueness
    my $unique_key = hex($1);
    $data{$unique_key} = $line;
    $lastkey = $unique_key;
  } else {
    # Continue an old entry
    $data{$lastkey} .= $line;
  }
}
print $data{$_}, "\n" for (sort { $a <=> $b } keys %data);
0 голосов
/ 03 июня 2011

В одну сторону (без проверки)

perl -wne'BEGIN{ $key = " " x 10 }' \
    -e '$key = $1 if /(0x[0-9a-f]{8})/;' \
    -e 'printf "%s%.10d%s", $key, $., $_' \
    inputfile \
    | LC_ALL=C sort \
    | perl -wpe'substr($_,0,20,"")'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...