| 
			| 
					
						| rubin 
								Гость
 | 
								|  | «  : 15-06-2006 12:14 »  |  | 
 
 Добрый День. Есть у меня одна проблема, я написал парсер на Perl (беру файл, запихиваю его в массив, далее построчно регулярными выражениями распихиваю строки по хешам как мне надо, соблюдая структуру начального файла). Теперь когда у меня файл разложен в хеше его надо выгрузить в другой файл в том же виде и порядке как и в исходном файле, а вот как это сделать - понять не могу. Может кто подскажет?   #!/usr/bin/perl -wopen (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 | 
								|  | « Ответ #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 | 
								|  | « Ответ #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 | 
								|  | « Ответ #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 | 
								|  | « Ответ #7 : 16-06-2006 08:43 »  |  | 
 
 rubin, сложности себе придумывашь. Честно говоря, сохранение комментариев при машинном создании конфига - нонсенс. Видел чтоб конфиги сохранялись в таких случаях: просто вставка строк, изменение отдельных блоков (коменты в блоке удаляются). Но чтоб совсем сохранялись...
 Я поместил бы полезную инфу в хеш, которым удобно пользоваться в программе, а все прочее - в массив с мусором. Так же, сделал бы массив, в который заносил бы ссылки на эти элементы - для запоминания порядка. При сборке текстового конфига нужно будет пройти по массиву ссылок и собрать инфу в том же порядке. Так же нужно обнаружить новые и удаленные записи. Для удаленных нужно ли сохранять комменты?
 Это пока прикидка.
 
 Насчет порядка.
 Хеш не гарантирует (быстрее да же гарантирует обратное) порядок элементов, а массив - да. Есть модули поддержки упорядоченных хешей (как массивы в PHP).
 |  
						| 
								|  |  
								|  |  Записан | 
 
 ... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С. |  |  | 
	| 
			| 
					
						| rubin 
								Гость
 | 
								|  | « Ответ #8 : 16-06-2006 08:53 »  |  | 
 
 Для удаленных(на будущее) сохранять коменты не надо.Ведь проще удалить данный ключ, значением которого является хеш со значением и коментарием.
 А порядок перебора строк - по тому номеру который я присвоил внутри хеша.
 Как мне написать такой цикл, чтобы он делал так, как я описал в предыдущем сообщении???
 А с добавлением я буду разбираться позже. По сути у меня остались выходные на эту процедуру, в понедельник мне надо уже отдать рабочий код.
 Короче попа. Нагрузили так, что у меня уже голова кругом.
 |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| RXL | 
								|  | « Ответ #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 | 
								|  | « Ответ #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++ » |  Записан | 
 
 ... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С. |  |  | 
	|  |