Perl - разбирать блоки из текстового файла
Во-первых, я прошу прощения, если вы считаете, что это дубликат. Я огляделся и нашел несколько очень похожих вопросов, но я либо заблудился, либо это было не совсем то, что, на мой взгляд, мне нужно, и поэтому не смогло придумать правильную реализацию.
ВОПРОС:
Итак, у меня есть текстовый файл, который содержит записи, сделанные другим скриптом (я могу отредактировать формат для того, как эти записи генерируются, если вы можете предложить лучший способ их форматирования):
SR4 Pool2
11/5/2012 13:45
----------
Beginning Wifi_Main().
SR4 Pool2
11/8/2012 8:45
----------
This message is a
multiline message.
SR4 Pool4
11/5/2012 14:45
----------
Beginning Wifi_Main().
SR5 Pool2
11/5/2012 13:48
----------
Beginning Wifi_Main().
И я сделал Perl-скрипт для разбора файла:
#!C:\xampp-portable\perl\bin\perl.exe
use strict;
use warnings;
#use Dumper;
use CGI 'param','header';
use Template;
#use Config::Simple;
#Config::Simple->import_from('config.ini', \%cfg);
my $cgh = CGI->new;
my $logs = {};
my $key;
print "Content-type: text/html\n\n";
open LOG, "logs/Pool2.txt" or die $!;
while ( my $line = <LOG> ) {
chomp($line);
}
print $logs;
close LOG;
Моя цель - получить в конце хеш, который будет выглядеть так:
$logs = {
SR4 => {
Pool2 => {
{
time => '11/5/2012 13:45',
msg => 'Beginning Wifi_NDIS_Main().',
},
{
time => '11/8/2012 8:45',
msg => 'This message is a multiline message.',
},
},
Pool4 => {
{
time => '11/5/2012 13:45',
msg => 'Beginning Wifi_NDIS_Main().',
},
},
},
SR5 => {
Pool2 => {
{
time => '11/5/2012 13:45',
msg => 'Beginning Wifi_NDIS_Main().',
},
},
},
};
Что было бы лучшим способом добиться этого? Должен ли я изменить форматирование сгенерированных журналов, чтобы мне было проще? Если вам нужна дополнительная информация, просто спросите. Заранее благодарю.:)
3 ответа
Если вы можете вывести его в формате XML, его чтение будет очень просто с XML::Simple
Формат не имеет смысла. Вы использовали хэш на третьем уровне, но не указали ключи для значений. Я предполагаю, что это должен быть массив.
my %logs;
{
local $/ = ""; # "Paragraph mode"
while (<>) {
my @lines = split /\n/;
my ($x, $y) = split ' ', $lines[0];
my $time = $lines[1];
my $msg = join ' ', @lines[3..$#lines];
push @{ $logs{$x}{$y} }, {
time => $time,
msg => $msg,
};
}
}
Должен ли я изменить форматирование сгенерированных журналов
Ваши метки времени кажутся неоднозначными. В большинстве часовых поясов час года повторяется.
Хотя идея Karthik T об использовании XML имеет смысл, и я бы тоже об этом подумал, я не уверен, что это лучший маршрут. Первая проблема заключается в том, чтобы поместить его в формате XML.
Во-вторых, формат XML может быть не так легко проанализирован. Конечно, модуль XML::Simple прочитает все это одним махом, тогда вам придется анализировать саму структуру данных XML.
Если вы можете установить вывод так, как хотите, сделайте его в формате, который легко разбирать. Мне нравится использовать префиксные идентификаторы данных. В следующем примере каждый фрагмент данных имеет свой собственный идентификатор. ER:
говорит мне, когда я достиг конца записи:
DT: 11/5/2012 13:35
SR: SR4
PL: Pool2
MG: Beginning Wifi_Main().
ER:
DT: 1/8/2012 8:45
SR: SR4
PL: Pool2
MG: This message is a
MG: multiline message.
ER:
Разбор этого вывода является прямым:
my %hash;
while ( $line = <DATA> ) {
chomp $line;
if ( not $line eq "ER:" ) {
my ($key, $value) = split ( ": ", $line );
$hash{$key} .= "$value "; #Note trailing space!
}
else {
clean_up_hash ( \%hash ); #Remove trailing space on all values
create_entry ( \%log, \%hash );
%hash = ();
}
}
Мне нравится использовать классы всякий раз, когда я начинаю получать сложные структуры данных, и я бы, вероятно, создал Local::Log
класс и подклассы для хранения каждого слоя журнала. Однако это не является абсолютной необходимостью и не было частью вашего вопроса. Тем не менее, я бы использовал create_entry
подпрограмма просто чтобы сохранить логику определения, где в вашем журнале эта запись принадлежит внутри вашего цикла.
ПРИМЕЧАНИЕ. Я добавляю пробел после каждого фрагмента данных. Я сделал это, чтобы сделать код проще, поскольку некоторые из ваших сообщений могут занимать более одной строки. Есть и другие способы справиться с этим, но я пытался сохранить цикл максимально чистым и с минимальным if
заявления как можно.