Perl - сортировка сложной структуры данных - PullRequest
0 голосов
/ 13 марта 2020

У меня есть старый perl проект, текстовый анализатор журнала событий, и я получил запрос на сортировку вывода по идентификатору события и удаление повторяющихся событий. Таким образом, анализатор читает текстовый файл и помещает каждое событие в массив. Каждое поле в массиве содержит ha sh с несколькими парами ключ -> значение. один ключ называется последовательностью и содержит номер события. Теперь я хочу отсортировать массив на основе значения последовательности каждого поля массива. Во-вторых, я хочу удалить повторяющиеся идентичные порядковые номера из массива.

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

open (my $mel, "<", $in_filename) or die "\nFile '$in_filename' does not exist or is not readable.\n";

my $i=0;
my $eventcount = 0;

while (<$mel>) {

        # Separate events by "Date/Time" :
        if (/^$/) {
            next;
        }
        if (/^Date\/Time:\s(.*)$/) {
            if ($eventcount >0) {
                $i++;
            }
            $eventcount++; # eventcount initialized with ‘0’
        }

        # Gathering information of the MEL event :
        if (/^Date\/Time:\s(.*)$/) {$MEL[$i]{date} = $1; next;}
        if (/^Sequence number:\s(\d+)$/) {$MEL[$i]{sequence} = $1; next;}
        if (/^Event type:\s([0-9|a-f|A-F]{1,6})$/) {$MEL[$i]{type} = lc $1; next;}
        if (/^Event category:\s(\w+)$/) {$MEL[$i]{category} = $1; next;}
        if (/^Priority:\s(\w+)/) {$MEL[$i]{priority} = $1; next;}
        if (/^Description:\s(.*)$/) {$MEL[$i]{description} = $1; next;}
        if (/^Event specific codes:\s(.*)$/) {$MEL[$i]{code} = $1; next;}
        if (/^Component location:\s(.*)$/) {$MEL[$i]{location} = $1; next;}
        if (/^Logged by:\s.*(.)$/) {$MEL[$i]{logged_by} = $1; next;}
        if (/^4[dD]\s45\s4[cC]\s48\s(\d\d)/) {$MEL[$i]{version} = hex $1;}

}

Пример события в текстовом файле:

Date/Time: 2/3/20, 12:18:20 PM
Sequence number: 200 <==============
Event type: 5023
Event category: Command
Priority: Informational
Event needs attention: false
Event send alert: false
Event visibility: true
Description: Controller return status/function call for requested operation
Event specific codes: b8/1/0
Component type: Controller
Component location: Shelf 99, Bay A
Logged by: Controller in bay A

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

Секунда Я хочу удалить поле из массива, когда значения ключа также существуют в другом поле массива.

Надеюсь, кто-то понимает, что мне нужно: -)

Возможно ли это?

Ответы [ 3 ]

1 голос
/ 13 марта 2020

Вы можете использовать пользовательский блок сортировки для сортировки массива:

my @sorted = sort { $a->{sequence} <=> $b->{sequence} } @MEL;

Но гораздо проще использовать ха sh хешей, чем массив хешей.

#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };

my $in_filename = ... ;
open my $mel, '<', $in_filename or die $!;

my %event;

my ($current, $id);
while (<$mel>) {

    next if /^$/;

    if (m{^Date/Time:\s(.*)$}) {
        if (defined $id) {
            $event{$id} = $current;
        }
        $current = { date => $1 };
    } elsif (/^Sequence number:\s(\d+)$/) {
        $id = $1;
    } elsif (/^Event type:\s([0-9|a-f|A-F]{1,6})$/) {
        $current->{type} = lc $1;
    } elsif (/^Event category:\s(\w+)$/) {
        $current->{category} = $1;
    } elsif (/^Priority:\s(\w+)/) {
        $current->{priority} = $1;
    } elsif (/^Description:\s(.*)$/) {
        $current->{description} = $1;
    } elsif (/^Event specific codes:\s(.*)$/) {
        $current->{code} = $1;
    } elsif (/^Component location:\s(.*)$/) {
        $current->{location} = $1;
    } elsif (/^Logged by:\s.*(.)$/) {
        $current->{logged_by} = $1;
    } elsif (/^4[dD]\s45\s4[cC]\s48\s(\d\d)/) {
        $current->{version} = hex $1;
    }
}

for my $e (sort { $a <=> $b } keys %event) {
    say 'Sequence number:', $e;
    for my $k (sort keys %{ $event{$e} }) {
        say "$k: $event{$e}{$k}";
    }
}

Это может быть дополнительно упрощено созданием одного большого регулярного выражения, чтобы соответствовать большинству деталей:

my $regex = qr/
               Event\ type:\s(?<type>[0-9|a-f|A-F]{1,6})$
              |Event\ category:\s(?<category>\w+)$
              |Priority:\s(?<priority>\w+)
              |Description:\s(?<description>.*)$
              |Event\ specific\ codes:\s(?<code>.*)$
              |Component\ location:\s(?<location>.*)$
              |Logged\ by:\s.*(?<logged>.)$
              |4[dD]\s45\s4[cC]\s48\s(?<version>\d\d)
/x;

while (<$mel>) {
    next if /^$/;

    if (m{^Date/Time:\s(.*)$}) {
        if (defined $id) {
            $current->{type} = lc $current->{type}
                if exists $current->{type};
            $current->{version} = hex $current->{version}
                if exists $current->{version};
            $event{$id} = $current;
        }
        $current = { date => $1 };
    } elsif (/^Sequence number:\s(\d+)$/) {
        $id = $1;
    } elsif (/^$regex/) {
        $current->{ (keys %+)[0] } = (values %+)[0];
    } else {
        warn "Skipping: $_";
    }
}
0 голосов
/ 14 марта 2020

Мой ответ основан на хорошем регулярном выражении @ choroba, но я думаю, что этот проще:

my $key = 'sequence';  #or other fields
my $keep = 'first';    #or 'last' record with identical $key

my $regex = qr{
   Date/Time:              \s* (?<date>.*)
  |Sequence\ number:       \s* (?<sequence>\d+)
  |Event\ type:            \s* (?<type>[0-9|a-f|A-F]{1,6})
  |Event\ category:        \s* (?<category>\w+)
  |Priority:               \s* (?<priority>\w+)
  |Description:            \s* (?<description>.*)
  |Event\ specific\ codes: \s* (?<code>.*)
  |Component\ location:    \s* (?<location>.*)
  |Logged\ by:             \s* (?<logged_by>.*)
  |4[dD]\s45\s4[cC]\s48\s(?<version>\d\d)
}x;

my @event=();
while (<>) {
  m{^Date/Time:} and push @event, {};
  m{^$regex}     and @{$event[-1]}{keys %+} = values %+;
}

#special treatment for type and version: hex and lc
exists $$_{type}    and $$_{type}    = hex $$_{type}    for @event;
exists $$_{version} and $$_{version} = lc  $$_{version} for @event;

#mark for deletion
my %exists; $exists{$$_{$key}}++ and $$_{delete}=1
   for $keep eq 'first' ? @event
     : $keep eq 'last'  ? reverse(@event)
     : die "keep must be first or last";

#delete those marked
@event = grep !$$_{delete}, @event;

#sort by $key
@event = sort { $$a{$key} <=> $$b{$key} } @event;

Я предположил, что тип должен быть hex ed, а версия должна быть lc ed, а не наоборот, как в вопросе.

Беги как:

perl script.pl input_file
0 голосов
/ 13 марта 2020

Описание проблемы неполное. Не ясно, являются ли записи однородными (все одного типа).

Хорошо, если приведенное выше предположение верно, тогда задача довольно проста.

Разделите файл на записи, затем заполните ha sh с помощью номера события в качестве ключа и запишите как дубликаты пропуска значений.

Затем выполните сортировку по ключу ha sh и выведите запись.

use strict;
use warnings;
use feature 'say';

my %events;
my %seen;
my $data = do { local $/; <DATA> };

$data =~ s!\n(Date/Time)!\n\n$1!g;

my @data = split '\n\n', $data;

for my $record (@data) {
    my $event = get_event_n( $record );

    next if $seen{$event};

    $seen{$event}   = 1;
    $events{$event} = $record;
}

say '----- Sorted Events -----';

for my $event (sort keys %events) {
    say $events{$event};
    say '-' x 45;                 # record separator as visual indicator
}

sub get_event_n {
    my $record = shift;
    my $sequence;

    $record =~ /Sequence number:\s+(\d+)/;
    $sequence = $1;

    return $sequence;
}

__DATA__
Date/Time: 2/3/20, 12:19:20 PM
Sequence number: 230
Event type: 5023
Event category: Command
Priority: Informational
Event needs attention: false
Event send alert: false
Event visibility: true
Description: Controller return status/function call for requested operation
Event specific codes: b8/1/0
Component type: Controller
Component location: Shelf 99, Bay A
Logged by: Controller in bay A
Date/Time: 2/3/20, 12:18:20 PM
Sequence number: 200
Event type: 5023
Event category: Command
Priority: Informational
Event needs attention: false
Event send alert: false
Event visibility: true
Description: Controller return status/function call for requested operation
Event specific codes: b8/1/0
Component type: Controller
Component location: Shelf 99, Bay A
Logged by: Controller in bay A
Date/Time: 2/3/20, 12:18:25 PM
Sequence number: 205
Event type: 5023
Event category: Command
Priority: Informational
Event needs attention: false
Event send alert: false
Event visibility: true
Description: Controller return status/function call for requested operation
Event specific codes: b8/1/0
Component type: Controller
Component location: Shelf 99, Bay B
Logged by: Controller in bay B
Date/Time: 2/3/20, 12:18:28 PM
Sequence number: 209
Event type: 5023
Event category: Command
Priority: Informational
Event needs attention: false
Event send alert: false
Event visibility: true
Description: Controller return status/function call for requested operation
Event specific codes: b8/1/0
Component type: Controller
Component location: Shelf 92, Bay B
Logged by: Controller in bay B
Date/Time: 2/3/20, 12:18:25 PM
Sequence number: 205
Event type: 5023
Event category: Command
Priority: Informational
Event needs attention: false
Event send alert: false
Event visibility: true
Description: Controller return status/function call for requested operation
Event specific codes: b8/1/0
Component type: Controller
Component location: Shelf 99, Bay B
Logged by: Controller in bay B
...