Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: (Perl) Как выгрузить в файл текст который лежит в анонимном хеше?  (Прочитано 15911 раз)
0 Пользователей и 1 Гость смотрят эту тему.
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 Offline
Пол: Мужской

WWW
« Ответ #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)/)
{
...
}
заменить на
Код:
next if (m/(^\s*$|^#)/);
На один блок меньше и запись короче.

Почитаю - еще напишу.


А по вопросу: последовательно перебираешь, проверяешь, выводишь...
« Последнее редактирование: 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)/)
{
...
}
заменить на
Код:
next if (m/(^\s*$|^#)/);
На один блок меньше и запись короче.

Почитаю - еще напишу.


А по вопросу: последовательно перебираешь, проверяешь, выводишь...
1) За оптимизацию спасибо.
2) Пустые строки мне нужны, так как потом надо будет все содержимое хеша с пустыми строками выкинуть в др. файл.
Прочитал про рекурсивный метод, но как реализовать его пока не понимаю. Я вообще всего 1,5 недели язык учу, поэтому прошу понять Улыбаюсь
« Последнее редактирование: 06-12-2007 18:31 от Алексей1153++ » Записан
RXL
Технический
Администратор

Offline Offline
Пол: Мужской

WWW
« Ответ #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 Offline
Пол: Мужской

WWW
« Ответ #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 Offline
Пол: Мужской

WWW
« Ответ #7 : 16-06-2006 08:43 » 

rubin, сложности себе придумывашь. Честно говоря, сохранение комментариев при машинном создании конфига - нонсенс. Видел чтоб конфиги сохранялись в таких случаях: просто вставка строк, изменение отдельных блоков (коменты в блоке удаляются). Но чтоб совсем сохранялись...

Я поместил бы полезную инфу в хеш, которым удобно пользоваться в программе, а все прочее - в массив с мусором. Так же, сделал бы массив, в который заносил бы ссылки на эти элементы - для запоминания порядка. При сборке текстового конфига нужно будет пройти по массиву ссылок и собрать инфу в том же порядке. Так же нужно обнаружить новые и удаленные записи. Для удаленных нужно ли сохранять комменты?
Это пока прикидка.

Насчет порядка.
Хеш не гарантирует (быстрее да же гарантирует обратное) порядок элементов, а массив - да. Есть модули поддержки упорядоченных хешей (как массивы в PHP).
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
rubin
Гость
« Ответ #8 : 16-06-2006 08:53 » 

Для удаленных(на будущее) сохранять коменты не надо.
Ведь проще удалить данный ключ, значением которого является хеш со значением и коментарием.
А порядок перебора строк - по тому номеру который я присвоил внутри хеша.
Как мне написать такой цикл, чтобы он делал так, как я описал в предыдущем сообщении???
А с добавлением я буду разбираться позже. По сути у меня остались выходные на эту процедуру, в понедельник мне надо уже отдать рабочий код.
Короче попа. Нагрузили так, что у меня уже голова кругом.
Записан
RXL
Технический
Администратор

Offline Offline
Пол: Мужской

WWW
« Ответ #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 Offline
Пол: Мужской

WWW
« Ответ #10 : 18-06-2006 14:26 » new

Подумал тут еще чуток:
Код:
#!/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++ » Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines