Четверг, 28.11.2024, 16:12
Приветствую Вас Гость
Главная | Регистрация | Вход
Домик быта
Меню сайта
  • Главная страница
  • Информация о сайте
  • Обратная связь
  • Ремонт техники
  • Доска объявлений
  • Каталог статей
  • Каталог файлов
  • Каталог сайтов
  • Дневник
  • Форум
  • Гостевая книга
  • Всё для веб-мастера
  • Онлайн игры
  • Кино клипы музыка
  • Фотоальбомы
  • FAQ (вопрос/ответ)



  • Ремонт бытовой техники! Rambler's Top100

    Яндекс цитирования

    Рейтинг@Mail.ru

                 Полезные утилиты:
    SMS бесплатно С-Пб:

      Абонентам Билайн

      Абонентам МТС

      Абонентам Теле 2

      Абонентам Мегафон

      Абонентам Скайлинк

    Главная » Статьи » ПК Железо Soft » Вопрос-ответ "F1-Help" 2000-2002 г.

    Вопрос-ответ "F1-Help" 2002-18
    ЧАСТЬ 2.
    В прошлый раз я рассказывал о том, как и где можно получить нужную информацию по Perl, как устанавливать модули, и др. Сегодня мы рассмотрим способы борьбы с ошибками в программах, а также научимся некоторым хитростям программирования на языке Perl.


    Работа над ошибками

    Вопрос системному администратору:
    "Кто такой Генерал Failure и почему он не может прочитать мой диск?"
    Старая история

    Анализ ошибок программы позволяет лишний раз не засорять форумы глупыми вопросами, на которые следуют такие же ответы. Система сообщений об ошибках придумана очень давно, и главное ее назначение - показать программисту (или пользователю), где именно эта ошибка произошла (к сообщениям об ошибках Windows это не имеет никакого отношения - см. www.exler.ru ;-)). Кроме того, любой язык программирования позволяет перехватывать и обрабатывать всевозможные неприятности. В процессе написания программы расставлять обработчики ошибок нужно обязательно. В дальнейшем, часть из них можно и удалить. Но этого делать я бы не рекомендовал, так как человеку свойственно ошибаться, и если вам кажется, что в данном куске кода ошибки быть не может, это не является аксиомой. Приступим к охоте за ошибками.

    "Жы Шы пиши с буквой И". Синтаксические ошибки - нормальный атрибут процесса программирования. Забытая скобка, точка-с-запятой, очепятка... Различные редакторы с подсветкой кода помогают в обнаружении таких ошибок, но не все пользуются такими программами (или отключают подсветку). Поэтому неприятности появляются при запуске скрипта. Для локальной программы или cgi-скрипта на локальном сервере поиск ошибок не составляет особого труда. Сообщения выдаются либо в стандартный вывод, либо пишутся в лог-файл ошибок сервера. Здесь стоит пояснить пользователям Win32, что по причине "закрывательства" окна MS-DOS при завершении программы, следует запускать скрипты не двойным кликом, а открывать сеанс MS-DOS и запускать скрипт командной строкой как perl script.pl. В этом случае окно не закроется, и вы увидите сообщение об ошибке. С локальными программами разобрались, но как быть с cgi-скриптами, запускаемыми на сервере? На платных серверах часто предоставляется доступ к логам сервера, и это облегчает проблему, но не у всех же денег много! Здесь есть два выхода. Первый - попросить доступ к логам (вариант с самовольным получением доступа мы рассматривать не будем=)). Второй - проверять синтаксис на локальной машине. В этом случае можно использовать редакторы "Перл", а также отладчик "Перл". Запускается он в командной строке как perl -d script.pl. (подробнее perldoc perlrun, perldoc perldebtut , perldoc perldubug). ActiveState предлагает для отладки свой инструмент с графическим интерфейсом, который, после установки, запускается вместо стандартного консольного. В поставке "Перл" его нет, поэтому его следует скачать.

    "А чей-та оно так делает?". Каждая функция возвращает какое-либо значение (этим она от процедуры и отличается). Одни функции по своему назначению должны возвращать какой-либо результат (например hex, time, etc.), другие, выполняя поставленную задачу, на всякий случай шепотом говорят нам о том, как прошла ее работа. Вот этим и надо пользоваться!

    Примеры.

    Программе обязательно требуется некоторый файл, например файл конфигурации.

    ...

    open(CONF,"program.cfg") || die "File program.cfg error: $!";

    ...

    Теперь о том, что все это означает. Если слева от || - false, значит, выполнится то, что справа.

    Если функция open() не сработала, то она возвращает неопределенное значение (а это самое настоящее false) (perldoc -f open).

    || - это оператор "ИЛИ". Смысл его работы таков: слева и справа от него два выражения. Одно из них должно быть положительным, а другое отрицательным (здесь имеется в виду результат работы, а не числовое +1,-1).

    Он начинает проверять слева, и если находит положительное значение (true), значит, правую часть надо сделать отрицательной, то есть не запускать. А если находит false (0 или "" или неопределенность), значит, правую часть надо запустить. Таким же образом работает оператор && - "И", с той лишь разницей, что ему нужны слева и справа одинаковые значения. Подробнее эти операторы мы рассмотрим ниже (perldoc perlop).

    Функция die производит аварийное завершение программы с выводом ошибки в STDERR - стандартный вывод для ошибок (perldoc -f die).

    $! - это встроенная переменная "Перл", которая содержит информацию о последней произошедшей ошибке (perldoc perlvar).

    В связи с тем, что "Перл" имеет очень широкие "синтаксические" возможности, выше описанное выражение можно записать так:

    ...

    die "File program.cfg error: $!" if !open(CONF,"program.cfg");

    ...

    или так:

    ...

    die "File program.cfg error: $!" unless open(CONF,"program.cfg");

    ...

    а вот "расшифрованный" вариант написания.

    ...

    $result=open(CONF,"program.cfg");
    if(!$result){
      die "File program.cfg error: $!";
    }

    ...

    Таким образом, результаты работы функций можно хранить в различных переменных, массивах, etc., можно обрабатывать на лету и логировать ход выполнения программы. Несколько примеров:

    # Используем возврат функции open()

    # Для примера возьмём часть скрипта счётчика посещений,

    # который считает по дням отдельно.


    @date=(localtime)[5,4,3];


    # Функция localtime возвращает массив значений,

    # 3,4 и 5, элементы которого содержат соответственно

    # день месяца, ©месяца минус 1, год минус 1900.

    # Мы создаём новый массив из трёх элементов возвращаемого

    # функцией localtime массива. (perldoc -f localtime)


    $date[0]+=1900;
    $date[1]=sprintf("%02D",$date[1]+1);
    $date[0]=sprintf("%02D",$date[0]);


    # Приводим в порядок даты. Прибавляем к месяцу 1,

    # а к году 1900. Также приводим день и месяц к двухзначному

    # виду (MM,ДД) с помощью функции sprintf (perldoc -f sprintf)


    $today=join("-",);


    # Соединяем элементы массива в строку даты, разделяя их символом "-"

    # Получаем дату в виде ГГГГ-MM-ДД


    if(!open(F,"+<$today.dat")){
      open(F,">$today.dat");
    }


    # А вот и самое главное! Если файл существует, мы его открываем

    # на чтение и запись. Если файла нет, функция open вернёт false,

    # и тогда мы откроем файл на запись с созданием. (perldoc -f open)

    $count=;
    $count++;


    # Читаем число, прибавляем 1.

    # Если мы только что создали файл, чтения не произойдёт

    # и $count будет ="", но $count++ сделает переменную равной 1.


    seek(F,0,0);

    # Устанавливаем курсор в файле на самое начало.(perldoc -f seek)

    truncate(F,0);

    # Очищаем файл полностью (perldoc -f truncate)

    print F $count;
    close(F);

    # Пишем новое число и закрываем.

    Это пример, как можно использовать возвраты функций. Теперь примеры именно отлова ошибок разными способами на примере различных функций.

    open(F,"program.cfg") || catch_error("Config error","die");

    # то же самое:

    # !open(F,"program.cfg") && catch_error("Config error","die");


    open(F,$defaulttext)||catch_error("DefText error[$defaulttext]:","do not die");
    sub catch_error(){
      my ($error,$killme)=@_;
      open(F,">>program.log");
      print F "$error\n";
      close(F);
      exit if $killme eq "die";
    }


    Мы создаем функцию - обработчик ошибки. Обработчик пишет в лог, и если ошибка критическая, - завершает программу. В самой программе мы ставим обработчик для открытия файла конфигурации и для текстового файла, используемого программой по умолчанию. Невозможность открытия конфиг-файла - это критическая ошибка, и следует завершить программу. Открытие текстового файла в нашей программе не обязательно, поэтому мы не завершаем программу в случае ошибки. В качестве флага убить/не убить используем параметр в вызове функции, а в самой функции проверяем. Если он равен "die", - умираем.

    Предположим, в процессе выполнения нашей программы пользователь нажал Ctrl+C, то есть решил прервать программу. Эта комбинация посылает сигнал INT, обработчик которого мы можем определить с помощью специального хеша %SIG (perldoc perlvar):

    $SIG{INT}='IGNORE' # Проигнорировать
    $SIG{INT}=sub{
      # обработка
    }

    # или перенаправить на функцию stopsignal.

    # в которой мы сделаем обработку

    $SIG{INT}=\&stopsignal;

    Например, если в вашей программе при завершении необходимо сделать запись в файл или корректно отсоединиться от сервера, то такие обработчики просто обязательны. Сигналы-убийцы, например, могут быть посланы вашей cgi-программе сервером, если она слишком долго выполняется.

    Список всех сигналов вашей системы можно посмотреть так:

    foreach(keys %SIG){print "$_\n";}

    Теперь несколько слов об анализе ошибок. Как правило, сообщение об ошибке содержит описание оной и номер строки в вашей программе, где произошел сбой. В принципе, для программиста этого достаточно, но для начинающего маловато ;-). Поэтому последним необходимо найти на www.citforum.ru HTML версию книги В.В.Маслова "Perl. Учебный курс". Там имеются расшифровки сообщений об ошибках и предупреждений "Перл" на русском языке.

    Об ошибках в CGI-скриптах, вызывающих ошибку 500, мы поговорим позже, в соответствующей главе, а пока... о грустном достаточно.
    Маленькие хитрости

    "А я еще и на машинке шить умею..."
    Кот Матроскин

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

    Хитрые операторы. Об операторах || и && мы уже говорили, но мне хотелось бы рассказать о них подробнее. Что же можно из них состряпать? Очень много!# Эмулируем оператор switch из других языков, так как

    # в Perl его нет.

    while(<>){ # Читаем из стандартного ввода в $_ (perldoc perlvar)


      # /hello/ это тоже что $_=~m/hello/

      # Здесь оператор && проверяет истинность выражения слева и,

      # если оно верно, выполняет блок слева.


      /hello/ && do{
      print "Hi, man!\n";
      next;
      };


      # Функция next переводит выполнение сразу в начало цикла,

      # чтобы в случае выполнения блока, другие блоки проигнорировались

      # (perldoc -f next)


      /how are you/ && do{
      print "I'm fine, thanks!\n";
      next;
      };
      /bye/ && do{
      print "Good Luck, bro!\n";
      sleep 2;
      last;
      };

      # Функция last выводит нас из цикла (perldoc -f last)
    }

    # Предположим, что пользователь вводит информацию о себе,

    # а программа пытается его узнать. Для этого мы создаём

    # функцию для определения по фамилии, адресу или телефону.

    # test_user();

    # Описывать эту функцию не будем, а представим, что они проверяют

    # информацию из базы данных.

    # Каждая функция возвращает массив данных о пользователе если находит,

    # и ничего не возвращает, если не находит.

    #

    # Кроме того, у нас имеется функция goto_hell(), посылающая

    # пользователя к чёрту и завершает программу, если он нам не известен.

    print "Fio or Street or Phone->";
    chomp($enter=<>);
    test_user($enter,"FIO")||
      test_user($enter,"STREET")||
      test_user($enter,"PHONE")||goto_hell($fio);

    # Если программа доработала до этого момента, то....

    print "Hello, $_[0]!\n Street:$_[1]\n-Phone:$_[2]";

    # Наша программа последовательно ищет введённую строку

    # в столбцах базы FIO, STREET и PHONE и возвращает результат

    # в массив по умолчанию @_ (perldoc perlvar)

    # Если функция не находит пользователя, выводит пустое значение

    # которое переключает оператор || на правое выражение, и так по

    # цепочке операторов либо до нахождения пользователя, либо до

    # функции goto_hell, которая завершает программу.

    # Предположим, вы получаете пользовательские данные из формы

    # регистрации на сайте. Обязательными полями для успешной

    # регистрации являются логин, пароль и e-mail. В скрипте

    # мы принимаем все параметры в хеш $F{param}=value

    # Приём данных мы рассмотрим позже, а вот как мы проверим

    # наши поля. Пусть print_error - это наша функция, которая

    # выдаст страницу, сообщающую об ошибке заполнения формы.

    # Функция check_email проверяет правильность e-mail адреса

    # Функция check_word проверяет наличие недопустимых символов

    # Обе эти функции должны в случае ненахождения ошибки

    # вернуть положительное значение, пусть это будет "OK".

    print_error("Вы не заполнили обязательные поля!") unless($F{login} && $F{password} && $F{email});

    print_error("Введите правильный E-mail") if(!check_email($F{email}));

    print_error("Логин и пароль могут содержать только англ. буквы и цифры") if(!check_word($F{login}) || !check_word($F{login});

    Вот такие хитрые операторы. Надеюсь, вы поняли принцип их работы.

    Есть ещё оператор ! ("НЕ"), который меняет значение выражения, стоящего справа от него на противоположное.

    !open(F,"несуществующий файл"))

    # Вернёт положительный результат

    !open(F,"существующий файл"))

    # Вернёт отрицательный результат

    Вы наверняка уже используете оператор != (НЕ РАВНО) в своих программах. Есть такие операторы и с другими логическими функциями - ||= и &&=, ИЛИ-РАВНО и И-РАВНО соответственно.

    Принцип их работы такой:

    (Переменная = ИСТИНА) ИЛИ-РАВНА (ЗНАЧЕНИЕ)

    (Переменная = ИСТИНА) И-РАВНА (ЗНАЧЕНИЕ)

    Например, в HTML-форме пользователь не заполнил необязательное поле "field". Тогда нам нужно записать в соответствующую переменную значение по умолчанию.

    # Предполагаем, что параметры формы у нас в %DATA;

    $DATA{field} ||= "default value";

    Таким образом, если параметр не задан, он становится дефолтным.

    Пример второй. Допустим, в форме имеется поле select для выбора порядка сортировки по базе
    <select name=search>
    <option value=""> Прямая сортировка
    <option value="1"> Обратная сортировка
    </select> 

    Полученные данные опять в %DATA

    $DATA{search} &&= "DESC";

    ...

    $req=$db->prepare("SELECT......... $DATA{search}");

    Таким образом, если переменная = "", в запросе вместо $DATA{search} подставится пустота, а если выбрана обратная сортировка, подставится "DESC".

    Широта использования этих операторов ограничена лишь фантазией программиста ;-)

    Анонимные "элементы". Анонимные переменные, массивы, хеши, подпрограммы - это элементы программы, не имеющие имени. Обращение к ним происходит по ссылкам, хранящимся в других элементах.

    В Perl не существует многомерных массивов, как типа данных, в отличие от других языков. Многомерный массив в Перл представляет собой масив ссылок на анонимные массивы.

    $element=$array[$i][$j];

    Мы по ссылке, хранящейся в ячейке $i массива @array идем в анонимный массив и берем из него $j-ый элемент.

    Структуры из анонимных элементов могут быть самые изощренные.

    $element=$array[$i][$j][$k];
    $element=$array[$i]{$j}{"name"}{"job"};
    $element=$hash{$key}[$j]{$k}[$i];

    Для того, чтобы обратиться к анонимному элементу, входящему в состав сложной структуры, нужно разыменовать ссылку на него. Делается это так:

    @subarray=@{$array[$i][$j]};
    %subhash=%{$array[$i]{$j}{"name"}};
    %subhash=%{$hash{$key}[$j]{$k}};

    С кратким введением в "анонимы", пожалуй, закончу, и расскажу о некоторых хитростях их использования. Подробнее о них читайте в perldoc perlreftut, perldoc perlref.

    # Пример1.

    # Имеется таблица базы данных со столбцами:

    # ID, Фамилия, Должность, Зарплата, Стаж, Кол-во детей

    # ID-уникальный идентификатор, целое число

    # Нашей программе предстоит производить много операций

    # с прочитанными данными.


    while(@temp=get_db_line()){
      $P{$temp[0]}{family}=$temp[1];
      $P{$temp[0]}{job}=$temp[2];
      $P{$temp[0]}{salary}=$temp[3];
      $P{$temp[0]}{years}=$temp[4];
      $P{$temp[0]}{kids}=$temp[5];
    }

    # Функция get_db_line() должна выдавать строку таблицы

    # в виде массива. После прочтения всей таблицы мы можем

    # пользоваться хешем %P как структурой.

    @sort_zarplata=reverse sort {$P{$a}{sa-lary}<=>$P{$b}{salary}} keys %P'

    # Получим отсортированный по зарплате

    (от большей к меньшей)

    # список ID рабочих.

    Ещё один интересный элемент - анонимные подпрограммы. В главе про ошибки был такой пример:

    $SIG{INT}=sub{...};

    Ключу хеша присваивается ссылка на анонимную подпрограмму. Можно создать переменные, массив или хеш подпрограмм.

    $sub1=sub{...};
    $sub1->(); # Вызов
    @arraysubs=(\&subroutine1,
      \&subroutine2,
      sub{...},
      sub{...}
      );

    # Здесь элементы с номерами 0 и 1 содержат ссылки на реальные

    # подпрограммы, а элементы 2 и 3 - на анонимные, которые мы

    # определяем тут же

    $arraysubs[0]->(); # Вызов subroutine1
    $arraysubs[1]->(); # Вызов subroutine2
    $arraysubs[2]->(); # Вызов анонимной подпрограммы

    # В хешах

    %ACTIONS=( "login" => \&login,
      "exit" => sub{ exit; },
      "read" => \&read,
      ...
      );

    # Далее в программе мы принимаем параметр "action"

    # в переменную $action и ...

    $ACTION{$action}->(@params) if exists $ACTION{$action};

    # Таким образом освобождаясь от многочисленных if, elsif, else.

    Вообще, в Perl имеется столько разных неочевидных примочек, что даже самые продвинутые "перловщики" не знают всех хитростей. К тому же, многочисленные "Перл"-программеры мира не перестают их находить.

    Приведу еще один интересный пример. Предположим, наш большой скрипт выполняет много разных действий, в зависимости от входных параметров. Как правило, в локальных программах сообщения об ошибках выдаются пользователю сразу. Также программы могут записывать ошибки в лог-файлы. Разница между выводом ошибки юзеру и записи оной в лог состоит в том, что юзеру пишется понятная фраза типа "Файл не найден", а в лог можно записать различную дополнительную информацию, которая поможет исправить ошибку. Логирование ошибок очень полезно при написании CGI-скриптов. И главная задача в таком случае - не ограничиваться ...||die $!;. Пользователь должен получить понятное сообщение и продолжить работу со скриптом, а последний должен подробно описать ошибку в файле логов.

    # 1. Вариант ужасный

    ...

    if(!open(F,$file)){
      open(ERR,">>error.log");
      print ERR "Ошибка:файл $file";
      close(ERR);
      print "Content-type:text/html\n\n";
      ... # напечатка ошибки юзеру
    }

    # 2. Вариант хитрый

    ...
    logging_error("File error:$file!",-"print_login_form","Не найден файл") if !open(F,$file);
    ...

    # А вот главная функция в этом примере

    sub logging_error{
      my ($error,$sub,$message)=@_;
      # Далее логируем ошибку($error) с датой временем и т.д.
      ...
      ...
      # А вот и самое интересное:
      &{$sub}($message) if defined &{$sub};
    }

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

    Вызывается подпрограмма logging_error , которая принимает 3 параметра. Первый -сообщение, которое запишется в лог. Здесь удобно указывать точки возникновения ошибки. Второй параметр - имя подпрограммы, которой передается управленив после логирования ошибки, а третий параметр - сообщение для пользователя, которое распечатает подпрограмма из второго параметра.

    Запуск подпрограммы происходит в последней строке logging_error.

    &{$var} - такая конструкция подставляет вместо скобок строку, содержащуюся в $var. То есть:

    $var="myfunction";
    &{$var}("Hello,Perl!"); # Эквивалентно &myfunction("Hello,Perl!");
    sub myfunction(){
      my $text=shift;
      print $text;
    }

    Если параметры, указывающие на подпрограмму, не указаны или не соответсвуют существующей подпрограмме, запуск не произойдет. За это отвечает конструкция .... if defined &{$sub}

    Аналогичный эффект можно наблюдать с помощью конструкций: ${$varname}, @{$arrayname}, %{$hashname}

    Таким образом, функция logging_error примера становится универсальной и помогает сэкономить на размере кода.

    С абстрактным программированием ради программирования на этом закончим. В следующей части этого повествования мы перейдем к прикладному программированию и рассмотрим способы решения различных задач программирования и использования полезных модулей.

    По материалам журнала Компьютер Price (www.comprice.ru)

    Категория: Вопрос-ответ "F1-Help" 2000-2002 г. | Добавил: stachek36 (19.10.2008)
    Просмотров: 1194 | Рейтинг: 0.0/0
    Всего комментариев: 0
    Добавлять комментарии могут только зарегистрированные пользователи.
    [ Регистрация | Вход ]
    Форма входа
    Категории раздела
    Вопрос-ответ "F1-Help" 2000-2002 г. [7]
    Вопрос-ответ "F1-Help" 2003 г. [33]
    Вопрос-ответ "F1-Help" 2004 г. [24]
    Вопрос-ответ "F1-Help" 2005 г. [17]
    Вопрос-ответ "F1-Help" 2006 г. [49]
    Вопрос-ответ "F1-Help" 2007 г. [51]
    Вопрос-ответ "F1-Help" 2008 г. [44]
    Вопрос-ответ "F1-Help" 2009 г. [2]
    SOFT [3]
    Поиск
    Наш опрос
    Оцените мой сайт
    Всего ответов: 200
    Друзья сайта
  • Официальный блог
  • Сообщество uCoz
  • FAQ по системе
  • Инструкции для uCoz
  • Гоголь-Моголь
  • Статистика

    Онлайн всего: 5
    Гостей: 5
    Пользователей: 0
    Мобайл сервис
    Сочи, ул. Московская, 19 Режим работы:
    Пн-Вс: 10.00-18.00
    Без выходных
    Контакты:
    +7(988) 238-00-94
    //stachek36.ucoz.ru
    e-mail: stachek36@mail.ru
    Copyright MyCorp © 2024Сайт создан в системе uCoz