rubin
Гость
|
|
« : 15-06-2006 12:14 » |
|
Добрый День. Есть у меня одна проблема, я написал парсер на Perl (беру файл, запихиваю его в массив, далее построчно регулярными выражениями распихиваю строки по хешам как мне надо, соблюдая структуру начального файла). Теперь когда у меня файл разложен в хеше его надо выгрузить в другой файл в том же виде и порядке как и в исходном файле, а вот как это сделать - понять не могу. Может кто подскажет? #!/usr/bin/perl -w open (FILEHANDLE, "<services3.cfg"); # открываем файл для чтения @text = <FILEHANDLE>; # передаем в массив содержимое FILEHANDLE close (FILEHANDLE); %hash = (); # создаю пустой хеш $var = 0; $temp = 0; $val = "val"; $rem = "rem"; $keystone = ''; $open = 0; for ($loop_ind=0; $loop_ind<=$#text; $loop_ind++) # открываю цикл по строкам FILEHANDLE { #проверка пустой стоки текста if ($text[$loop_ind] =~ m/(^$)|(\x09)|([\ ]+\n)/) { if ($open == 0) {$var++; $hash{$var} = $text[$loop_ind]; } else { if ($num == 0) {$keystone = "Clear"; if ((ref $hash{$var}{$temp}{$num}) eq 'HASH') {$hash{$var}{$temp}{$num}{$keystone} -> {$rem} = $text[$loop_ind];} else {$hash{$var}{$temp}{$num} = {$keystone => {$rem => $text[$loop_ind]}};} } else {$keystone = "Clear"; $num++; $hash{$var}{$temp}{$num}{$keystone} -> {$rem} = $text[$loop_ind]; } } } else {# проверка строки на наличие символов "закрытия секции" if ($text[$loop_ind] =~ m/([\ ]+)*(\})/) {if ($text[$loop_ind] =~ m/(\#)/) {} else { if ($text[$loop_ind] =~ m/(\;)/) {# проверка строки на наличие символов "закрытия секции" с коментариями if ($text[$loop_ind] =~ m/([\ ]+)*(\})*([\ \x09]+)*(\;)*([\ \x09]+)*([\W\w\d\'\"\S\s\n]+)/) {$keystone = "Clear"; $num++; $hash{$var}{$temp}{$num}{$keystone} -> {$val} = $1.$2.$3.$4.$5.$6; $open = 0; } } else {$keystone = "Clear"; $num++; $hash{$var}{$temp}{$num}{$keystone} -> {$val} = $text[$loop_ind]; $open = 0; } } } else {# проверка строки на наличие "шарпа" - вся строка записывается как ремарка if ($text[$loop_ind] =~ m/(\#)/) {if ($open == 0) {$var++; $hash{$var} = $text[$loop_ind]; } else {if ($num == 0) {$keystone = "Sharp"; if ((ref $hash{$var}{$temp}{$num}) eq 'HASH') {$hash{$var}{$temp}{$num}{$keystone} -> {$rem} = $text[$loop_ind];} else {$hash{$var}{$temp}{$num} = {$keystone => {$rem => $text[$loop_ind]}};} } else {$keystone = "Sharp"; $num++; $hash{$var}{$temp}{$num}{$keystone} -> {$rem} = $text[$loop_ind]; } } } else {# проверка строки на предмет начала секции if ($text[$loop_ind] =~ m/(define)*(\ )*([A-Za-z]+) *(\{)/) {if ($text[$loop_ind] =~ m/(\;)/) {# проверка строки на предмет начала секции с коментариями if ($text[$loop_ind] =~ m/(define)*(\ )*([A-Za-z]+) *(\{)*([\ \x09]+)*(\;)*([\ \x09]+)*([\W\w\d\'\"\S\s\n]+)/) {$var++; $num = 0; $open = 1; $temp = $1.$2.$3.$4.$5.$6.$7.$8; $hash{$var}{$temp} = {$num =>''}; } } else {$var++; $num = 0; $open = 1; $temp = $1.$2.$3.$4; $hash{$var}{$temp} = {$num =>''}; } } else {# проверка строки пренадлежащей к секции на наличие коментариев if ($text[$loop_ind] =~ m/(\;)/) {# парсинг строки с коментариями if ($text[$loop_ind] =~ m/([\ \x09]+)*([A-Za-z\_\-]+)*([\ \x09]+)*([A-Za-z\d\_\-\!\%\:\"\,\.\n]+)*([\ \x09]+)*(\;)*([\ \x09]+)*([\W\w\d\'\"\S\s\n]+)/) {if ((ref $hash{$var}{$temp}{$num}) eq 'HASH') {$keystone = $2; $num++; $hash{$var}{$temp}{$num}{$keystone} -> {$val} = $4; $hash{$var}{$temp}{$num}{$keystone} -> {$rem} = $8; } else {$keystone = $2; if ($num == 0) {$hash{$var}{$temp}{$num} = {$keystone => {$val => $4}}; $hash{$var}{$temp}{$num}{$keystone} -> {$rem} = $8; } else {$num++; $hash{$var}{$temp}{$num} = {$keystone => {$val => $4}}; $hash{$var}{$temp}{$num}{$keystone} -> {$rem} = $8; } } } } else {# парсинг строки без коментариев if ($text[$loop_ind] =~ m/([\ \x09]+)*([A-Za-z\_\-]+)*([\ \x09]+)*([A-Za-z\d\_\-\!\%\:\"\,\.\n]+)/) {if ((ref $hash{$var}{$temp}{$num}) eq 'HASH') {$keystone = $2; $num ++; $hash{$var}{$temp}{$num}{$keystone} -> {$val} = $4; $hash{$var}{$temp}{$num}{$keystone} -> {$rem} = "\n"; } else {$keystone = $2; if ($num == 0) {$hash{$var}{$temp}{$num} = {$keystone => {$val => $4}}; $hash{$var}{$temp}{$num}{$keystone} -> {$rem} = "\n"; } else {$num ++; $hash{$var}{$temp}{$num} = {$keystone => {$val => $4}}; $hash{$var}{$temp}{$num}{$keystone} -> {$rem} = "\n"; } } } } } } } } }
При этом содержимое исходного файла services3.cfg такое: # test message
define service{ ; sdfsadf name generic-service ; The 'name' of this service template, referenced in other service de active_checks_enabled 1 ; Active service checks are enable register 0 ; DONT REGISTER THIS DEFINITION - ITS NOT A REAL SERVICE, JUST A TEMPLATE! } ; temp message
# test message
define service{ use generic-service host_name router-fcu }
# Service definition #define service{ # use generic-service ; Name of service template to use # # notification_period 24x7 # }
Секция начинается с "define service{", а заканчивается " }". Собственно как мне записать распарсенный текст в таком же виде построчно выкинув его в новый файл format.cfg??? Все сто стоит после шарпа "#" - это ремарка или просто коментарий.
|
|
« Последнее редактирование: 06-12-2007 18:30 от Алексей1153++ »
|
Записан
|
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #1 : 15-06-2006 15:31 » |
|
Пока все не смотрел, но уже по началу могу предложить оптимизацию: 1) open (FILEHANDLE, "<services3.cfg"); # открываем файл для чтения @text = <FILEHANDLE>; # передаем в массив содержимое FILEHANDLE close (FILEHANDLE); for ($loop_ind=0; $loop_ind<=$#text; $loop_ind++) # открываю цикл по строкам FILEHANDLE { .... } Меняем на open (FILEHANDLE, "<services3.cfg") or die "can't open file"; while ($text = <FILEHANDLE>) { .... } close (FILEHANDLE); Зачем двойная работа и двойной расход памяти?.. И контроль ошибок не помешает. 2) отфильтровать ненужные строки: if ($text[$loop_ind] =~ m/(^$)|(\x09)|([\ ]+\n)/) { ... } заменить на На один блок меньше и запись короче. Почитаю - еще напишу. А по вопросу: последовательно перебираешь, проверяешь, выводишь...
|
|
« Последнее редактирование: 06-12-2007 18:30 от Алексей1153++ »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
rubin
Гость
|
|
« Ответ #2 : 16-06-2006 05:59 » |
|
Пока все не смотрел, но уже по началу могу предложить оптимизацию: 1) open (FILEHANDLE, "<services3.cfg"); # открываем файл для чтения @text = <FILEHANDLE>; # передаем в массив содержимое FILEHANDLE close (FILEHANDLE); for ($loop_ind=0; $loop_ind<=$#text; $loop_ind++) # открываю цикл по строкам FILEHANDLE { .... } Меняем на open (FILEHANDLE, "<services3.cfg") or die "can't open file"; while ($text = <FILEHANDLE>) { .... } close (FILEHANDLE); Зачем двойная работа и двойной расход памяти?.. И контроль ошибок не помешает. 2) отфильтровать ненужные строки: if ($text[$loop_ind] =~ m/(^$)|(\x09)|([\ ]+\n)/) { ... } заменить на На один блок меньше и запись короче. Почитаю - еще напишу. А по вопросу: последовательно перебираешь, проверяешь, выводишь... 1) За оптимизацию спасибо. 2) Пустые строки мне нужны, так как потом надо будет все содержимое хеша с пустыми строками выкинуть в др. файл. Прочитал про рекурсивный метод, но как реализовать его пока не понимаю. Я вообще всего 1,5 недели язык учу, поэтому прошу понять
|
|
« Последнее редактирование: 06-12-2007 18:31 от Алексей1153++ »
|
Записан
|
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #3 : 16-06-2006 06:53 » |
|
Рекурсией тут и не пахнет.
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
rubin
Гость
|
|
« Ответ #4 : 16-06-2006 07:09 » |
|
Если будет минутка, не напишите, как мне начать построчно, обязательно по порядку (с 0 до конца) выводить хеш в файл. Вообще если в дебагере запустить мой код, то структура хеша такая: %hash = ... 1 = '\n' 2 = '#define service{\n' 3 = '# name generic-service ; The \\'name\\' of this service template, \n' 4 = '# active_checks_enabled 1 ; Active service checks are enabled\n' 5 = '# жЕДЕТБМШОЩК гЕОФТ хРТБЧМЕОЙС\n' 6 = ' \n' 7 = '\n' 8 = HASH(0x1d2ae98)... 'define service{' = HASH(0x1d2aee0)... 0 = HASH(0x1ae8020)... 'use' = HASH(0x1ae7e7c)... 'rem' = 'Test rem\n' 'val' = 'generic-service' 1 = HASH(0x1ae7bb8)... 'host_name' = HASH(0x1cd6298)... 'rem' = 'woeirjwerfwf\n' 'val' = 'router-fcu' 10 = HASH(0x1d2add8)... 'contact_groups' = HASH(0x1ae8200)... 'rem' = '\n' 'val' = 'admins\n' 11 = HASH(0x1ae7cd8)... 'notification_interval' = HASH(0x1ae7f3c)... 'rem' = '\n' 'val' = 120\n 12 = HASH(0x1d2ae08)... 'notification_period' = HASH(0x1ae8668)... 13 = HASH(0x1d2aec8)... 14 = HASH(0x1d2af40)... 15 = HASH(0x1d2afa0)... 2 = HASH(0x1d5a970)... 3 = HASH(0x1ae7d08)...Cut... 4 = HASH(0x1d2adb4)...Cut... 5 = HASH(0x1d36948)...Cut... 6 = HASH(0x1ae8248)...Cut... 7 = HASH(0x1ae8404)...Cut... 8 = HASH(0x1ae8734)...Cut... 9 = HASH(0x1ae8998)...Cut... 9 = HASH(0x1d2afdc)...Cut...
Так вот мне надо в новый файл сначала выкинуть строку "1 = '\n'" проверив ее, является ли она хешем, если нет то вывести ее в опр. формате, когда дело дойдет до строки "8 = HASH(0x1d2ae98)...", то при проверке на хеш, заходим в него, проверяем является ли "'define service{' = HASH(0x1d2aee0)..." хешем, если да то снова заходим в него, и т.д. пока не наткнемся на НЕ хеш. причем выводить строки мне надо по номерам(в данном случае с 1-7, потом всю внутрянку 8-ого (с 0 - 15), потом внутр 9 и т.д. Что делать то я знаю, а вот как это писать понять не могу!!! Хелп ми, пожалуйста.
|
|
« Последнее редактирование: 06-12-2007 18:32 от Алексей1153++ »
|
Записан
|
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #5 : 16-06-2006 07:28 » |
|
rubin, я как раз схватил насморк и мне будет чем заняться пока болею А пока я на работе... Распиши требования к программе. Как я понял, ты хочешь сохранить все комментарии?
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
rubin
Гость
|
|
« Ответ #6 : 16-06-2006 07:52 » |
|
Значится так: есть конфигурационный файл вида: #define service{ # name generic-service ; The 'name' of this service template, referenced in other service de # active_checks_enabled 1 ; Active service checks are enabled # жЕДЕТБМШОЩК гЕОФТ хРТБЧМЕОЙС
define service{ use generic-service ;Test rem host_name router-fcu ; woeirjwerfwf
service_description PING is_volatile 0 #sgfsdgfsdf check_period 24x7 max_check_attempts 3 normal_check_interval 5 retry_check_interval 1 contact_groups admins notification_interval 120 notification_period 24x7 notification_options c,r check_command check_ping!100.0,20%!500.0,60% } define service{ use generic-service host_name switch-fcu service_description PING is_volatile 0 check_period 24x7 ; ergewrt max_check_attempts 3 ; ewrtewrtew normal_check_interval 5 ;werttu938475ljtle retry_check_interval 1 ; 03945ojijasfd contact_groups admins notification_interval 120 notification_period 24x7 notification_options c,r check_command check_ping!100.0,20%!500.0,60% }
define service{ - это начало секции, "use generic-service" - это ключ и значение, как видно из предыдущего сообщения "generic-service" - тоже хеш, где есть значение val = "generic-service", а rem = remark, причем ремарок может и не быть. Внутри секции могут быть строки с #( шарпами ) - это как принято у админов коментарии. Программа у меня всю инфу загоняет в анонимный хеш, длина которого не известна, так как количество секций в конфиге не известно. Мне осталось выкинуть содержимое файла в том же виде, в котором он и забирал из конфига!!! Со всеми коментариями, сносками и пустыми строчками т.д. Для того чтобы это осуществить я ввел нумерацию первого хеша, а так же нумерацию внутри хеша.
|
|
« Последнее редактирование: 06-12-2007 18:33 от Алексей1153++ »
|
Записан
|
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #7 : 16-06-2006 08:43 » |
|
rubin, сложности себе придумывашь. Честно говоря, сохранение комментариев при машинном создании конфига - нонсенс. Видел чтоб конфиги сохранялись в таких случаях: просто вставка строк, изменение отдельных блоков (коменты в блоке удаляются). Но чтоб совсем сохранялись...
Я поместил бы полезную инфу в хеш, которым удобно пользоваться в программе, а все прочее - в массив с мусором. Так же, сделал бы массив, в который заносил бы ссылки на эти элементы - для запоминания порядка. При сборке текстового конфига нужно будет пройти по массиву ссылок и собрать инфу в том же порядке. Так же нужно обнаружить новые и удаленные записи. Для удаленных нужно ли сохранять комменты? Это пока прикидка.
Насчет порядка. Хеш не гарантирует (быстрее да же гарантирует обратное) порядок элементов, а массив - да. Есть модули поддержки упорядоченных хешей (как массивы в PHP).
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
rubin
Гость
|
|
« Ответ #8 : 16-06-2006 08:53 » |
|
Для удаленных(на будущее) сохранять коменты не надо. Ведь проще удалить данный ключ, значением которого является хеш со значением и коментарием. А порядок перебора строк - по тому номеру который я присвоил внутри хеша. Как мне написать такой цикл, чтобы он делал так, как я описал в предыдущем сообщении??? А с добавлением я буду разбираться позже. По сути у меня остались выходные на эту процедуру, в понедельник мне надо уже отдать рабочий код. Короче попа. Нагрузили так, что у меня уже голова кругом.
|
|
|
Записан
|
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #9 : 18-06-2006 11:23 » |
|
rubin, спасибо за возможность потренироваться - давно на perl-е не писал. Держи чего хотел: out_conf(\%hash);
sub out_conf { my $conf = shift;
foreach $line (sort {$a <=> $b} keys %{$conf}) { if (!ref($conf->{$line})) { my $t = $conf->{$line}; chomp $t; print "$t\n"; } else { my $service = $conf->{$line}; my $name = (keys %{$service})[0]; $service = $service->{$name}; chomp($name); print $name, "\n"; foreach $line (sort {$a <=> $b} keys %{$service}) { my $t = $service->{$line}; my $name = (keys %{$t})[0]; $t = $t->{$name}; chomp($name);
if ($name eq 'Clear') { print "\t}"; if (defined $t->{'rem'}) { my $rem = $t->{'rem'}; chomp ($rem); print " ; ", $rem; } } else { my $val = $t->{'val'}; chomp($val); print "\t", $name, "\t", $val; if (defined $t->{'rem'}) { my $rem = $t->{'rem'}; chomp ($rem); print " ; ", $rem; } } print "\n"; } } } } Согласись, коряво. И парсер и, соотв., вывод. Вот результат: # test message
define service{ ; sdfsadf name generic-service ; The 'name' of this service template, referenced in other service de active_checks_enabled 1 ; Active service checks are enable register 0 ; DONT REGISTER THIS DEFINITION - ITS NOT A REAL SERVICE, JUST A TEMPLATE! }
# test message
define service{ use generic-service ; host_name router-fcu ; }
# Service definition #define service{ # use generic-service ; Name of service template to use # # notification_period 24x7
diff -b выдал полное совпадение без учета пробелов Итог: полный незачет! Такой программой пользоваться невозможно.Внешние признаки качественной программы на perl: удобство и компактность. Программа такого типа должна быть раза в 3 меньше и проще в чтении (твоя совсем не читабельна). Удобство: посмотри на прмере моего кода, как можно работать с твоим пропарсеным конфигом - повеситься можно! Обрати внимание на chomp() - ты совсем не очистил строки от символов перевода строки. Нормальный вид должен быть такого типа: $value = %conf{'service_name'}{'option_name'} Вывод: тебе нужно читать о perl и много экспериментировать.
|
|
« Последнее редактирование: 06-12-2007 18:34 от Алексей1153++ »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #10 : 18-06-2006 14:26 » |
|
Подумал тут еще чуток: #!/usr/bin/perl
sub parseConfig { my $filename = shift; my $FH;
if (defined $filename) { open $FH, '<', $filename or die ("ERROR: $!\n"); } else { $FH = STDIN; }
my @services = (); my $service; my $mode = ''; my @order = ();
L_next_line: while (<$FH>) { chomp;
if ($mode eq '') { m/^\s*($|[;#])/ && do # empty line or comment { push @order, $_; next L_next_line; };
m/^\s*define\s+service\s*{\s*([;#].*)?$/i && do # service open { $service = {}; push @order, {'obj' => $service, 'rem' => [['{', $1]]}; $mode = 'service'; next L_next_line; }; } elsif ($mode eq 'service') { m/^\s*($|[;#])/ && do # empty line or comment { push @{$order[$#order]{'rem'}}, ['', $_]; next L_next_line; };
m/^\s*}\s*([;#].*)?$/ && do # service close { push @services, $service; push @{$order[$#order]{'rem'}}, ['}', $1]; $mode = ''; next L_next_line; };
m/^\s*([-\w]+)\s+([^\s;#]+)\s*([;#].*)?$/ && do # service data { $service->{$1} = $2; push @{$order[$#order]{'rem'}}, [$1, $3]; next L_next_line; }; } else { die "ERROR: wrong \$mode: $mode\n"; } print STDERR "Unrecognized string: $_\n"; push @order, $_; }
close $FH;
return \@services, \@order; }
sub storeConfig { my ($services, $order, $filename) = @_; my $FH;
die ("storeConfig(): invalid arguments\n") if (ref($services) ne 'ARRAY' or ref($order) ne 'ARRAY');
if (defined $filename) { open $FH, '>', $filename or die ("ERROR: $!\n"); } else { $FH = STDOUT; }
foreach my $line (@{$order}) { if (!ref($line)) { print("$line\n") } elsif (ref($line) eq 'HASH') { my $service = $line->{'obj'}; my $lines = $line->{'rem'};
foreach $line (@{$lines}) { my ($name, $rem) = @{$line};
if ($name eq '{') { print $FH "define service {$rem\n"; } elsif ($name eq '}') { print $FH "}$rem\n"; } elsif ($name eq '') { print $FH "$rem\n"; } else { print $FH "\t$name\t$service->{$name}$rem\n"; } } } else { die "storeConfig(): invalid data type\n"; } }
close $FH; }
my ($s, $o) = parseConfig 'config';
storeConfig $s, $o; Не хватает еще поддержки изменений. Я думаю ее логика такая: 1. При выводе сервиса проверять присутствие $name - это удаленные из сервиса данные. Их или не выводить, или вывести как комментарий, с подстановкой соотв. символа перед строкой. 2. Новые строки добавить в конец сервиса. 3. Далее вывести отсутствующие в $order сервисы.
|
|
« Последнее редактирование: 06-12-2007 18:36 от Алексей1153++ »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
|