22 октября 2019 года    
Вторник | 12:49    
Главная
 Новости
Базы данных
Безопасность PC
Всё о компьютерах
Графика и дизайн
Интернет-технологии
Мобильные устройства
Операционные системы
Программирование
Программы
Связь
Сети
 Документация
Статьи
Самоучители
 Общение
Форум







Разделы / Интернет-технологии / Perl

Cборник часто задаваемых вопросов по языку PERL и программированию для WEB

CБОРНИК ЧАСТО ЗАДАВАЕМЫХ ВОПРОСОВ ПО ЯЗЫКУ PERL И ПРОГРАММИРОВАНИЮ ДЛЯ WEB

Авторы ответов:
Павел Аммосов apv@i-connect.ru
Артур Пенттинен artur@elma.fi
Михаил Поляков polk@inforis.nnov.su
Григорий Строкин grg@philol.msu.ru

Текущий координатор: Павел Аммосов
Составление FAQ начато: 20 окт 1998
Текущаяя версия от: 17 Apr 1999 22:16
Адрес FAQ в Internet: http://isabase.philol.msu.ru/~apavel/ruperl/

Общий план

Часть A. Перл как язык
Часть Б. Перл как средство для работы с CGI
Часть C. Perl и базы данных

Часть С. Perl и базы данных


  • (1) БД на плоских файлах
  • (Q 1.1) Я хочу какую-нибудь простейшую БД и прямо сейчас!
  • (Q 1.2) Можно ли как-нибудь из Perl получить доступ к dbf файлам?
  • (Q 1.3) А к MS access .mdb?
  • (Q 1.4) Зачем и как нужно запирать (lock) файлы?

  • (2) Реляционные, пост-реляционные и объектно-реляционные СУБД
  • (Q 2.1) Как использовать DBI?
  • (Q 2.2) Как работать с записями кусочками: первые N записей, следующие N...?
  • (Q 2.2) Где взять документацию/учебник по SQL?

    * * *

    (1) БД на плоских файлах

    (Q 1.1) Я хочу какую-нибудь простейшую БД и прямо сейчас!
    (A) Вы можете использовать простой текстовый файл с разделителями. Например, если мы пишем нечто типа телефонной книги, то вполне вероятно предположить, что ни в чьем имени, ни в номере телефона не встретится последовательность ::, так что именно ее и можно использовать в качестве разделителей.
    Файл с данными может выглядеть так:

    Иванов И.И.::888-0000::Какая-то улица, 17, кв 40
    Сидоров П.И.::888-8429::Другая улица, 5, кв 21
    ...... и тд.
    
    тогда программа, которая читает данные, может быть примерно такого вида:
    #!/usr/bin/perl
    # открываем файл
    open DATA, 'data.dat' or die "Невозможно открыть data.dat: $!";
    
    # читаем построчно из файла
    while (<DATA>) {
            chomp; # удаление символа конца строки
            # теперь в $_ есть строка и мы ее разделяем на переменные
            ($name, $phone, $address) = split(/::/);
            # и выведем на печать
            print "Имя: $name, телефон: $phone, адрес: $address\n";
    }
    close DATA;
    #всё
    
    Больше проблем возникает в случее, если надо удалить или отредактировать запись, но и их можно довольно просто и элегантно решить, если использовать механизм редактирования на месте (inplace edit) -- при использовании операции "ромб"(<>), можно читать из одного файла, а писать в другой:
    #!/usr/bin/perl 
    $^I = '~'; # запускаем inplace edit
    while (<>) { # Обратите внимание, что мы не открывали файл: при такой
            #конструкции имя файла берется из коммандной строки
            chomp;
            ($name, $phone, $address) = split(/::/);
            if (.... некоторое условие, при котором мы оставляем наши данные ... )
            {
                    print "$name::$phone::$address\n"; # теперь данные есть в новом файле
            }
    }
    # конец примера
    
    если запустить это программу как

    change.pl data.dat

    ,то в текущем каталоге будут два файла: data.dat, с записями, которые удовлетворили нашим условиям и data.dat~ -- предыдущая копия.
    (также, во многих случаях, всю программу такого типа можно записать как one-liner:

            perl -i~ -n -e 'print if(... условие)'
    )

    Двоичные файлы

    Для чтения двоичных файлов в Perl можно использовать функции read и unpack. К примеру, если использовать двоичный файл для хранения телефонной книги такого формата:

            40 символов -- фамилия, И.О. 
            10 символов -- номер телефона, 
            60 cимволов -- адрес,
    
    то строка описания формата для unpack будет выглядеть так:
    $format_str = 'A40 A10 A60';
    
    а сама программа, аналогичная первому примеру:
    #!/usr/bin/perl
    $format_str = 'A40 A10 A60';
    open DATA, 'binary.dat' or die "$!";
    while (read(DATA, $buf, 40+10+60)) { # <DATA> не покатит: такая
    # конструкция будет читать до символа перевода строки, а это не то, что нужно
            ($name, $phone, $address) = unpack($format_str, $buf);
            # Теперь в $name, $phone, $address есть данные и с ними можно делать
            # все, что захочется
    }
    close DATA;
    
    Чтобы вывести в файл такую запись можно использовать конструкцию типа
    print FILE pack($format_str, $name, $phone, $address);
    
    (Q 1.2) Можно ли как-нибудь из Perl получить доступ к dbf файлам?
    (A) Да, можно. На http://www.fi.muni.cz/~adelton/ есть модуль XBase, который позволяет читать/писать dbf. При чтении он даже поддерживает индексы. Кроме того, в комплект поставки также входит модуль DBD::XBase, при помощи которого можно даже оперировать dbf на SQL.

    (Q 1.3) А к MS access .mdb?
    (A) К файлам MS Access нельзя обращаться из perl напрямую, по крайней мере, в настоящее время.
    К MS Access можно обращаться по ODBC, при помощи DBD::ODBC.

    (Q 1.4) Зачем и как нужно запирать (lock) файлы? (A) Представьте себе ситуацию когда одновременно работают несколько копий одной и той же программы (к примеру, cgi-скрипты, обслуживающие запросы), читающие/пишушие в один файл, тогда рано или поздно создатся ситуация при которой один скрипт прочитал данные, произвел над ними некоторые действия и собрался записать их назад в файл, но в это же время другой скрипт тоже прочитал данные, тоже произвел над ними действия, но (!) он прочитал старые данные, которые он и запишет поверх данных, выданных другим скриптом. Таким образом, в файле остануться данные записаные одним из скриптов -- в лучшем случае, в худшем -- структура файла будет испорчена. Чтобы этого избежать в Unix и большинстве других ОС есть системный вызов flock(2) или аналогичный.

    Как использовать flock
    К примеру, скрипт который записывает имена вызывающих хостов в файл. (На деле такой список, конечно, можно получить из журнала регистрации web-сервера).

    #!/usr/bin/perl
    use Fcntl; # Импорт констант
    open (HOSTS, '>>hosts.log'); # Файл открыт для добавления записи
    flock(HOSTS, LOCK_EX); 
    # Теперь файл заблокирован: Если любой другой скрипт тоже вызовет flock на
    # этом файле, его flock не вернет управление в программу, пока мы не
    # разблокируем файл. Обратите внимание: flock -- декларативная функция, если
    # один из скриптов ее не использует при записи, то вся ваша блокировка не
    # работает.
    print HOSTS $ENV{REMOTE_HOST}, "\n"; # записали строку
    close HOSTS; # Файл при закрытии разблокируется автоматически
    
    # Вывести сообщение для пользователей
    print "Content-Type: text/plain\n\n";
    print "Название вашего хоста записано\n";
    
    Более подробный рассказ о flock и пример доступны на http://w3.stonehenge.com/merlyn/WebTechniques/col04.html

    Чего делать на системах, где нет flock или для блокировок файлов на сетевых дисках?

    Судя по perlfaq5(1), можно использовать модуль File::Lock с CPAN.

    * * *

    (2) Реляционные, пост-реляционные и объектно-реляционные СУБД

    (Q 2.1) Как использовать DBI?
    (A) DBI -- это интерфейс прикладных программ к СУБД, использующим SQL в качестве языка запросов. Cам DBI определяет только набор функций, переменных и соглашений. Вся непосредственная работа выполняется Database Drivers (DBD) -- модулями, обеспечивающими связь с СУБД. DBI только обеспечивает стандартный интерфейс для этих драйверов.

    Полная схема архитектуры при работе DBI выглядит примерно так:


    | Прикладная программа |
    | DBI |
    | DBD |
    | СУБД |
    Подсоединение к СУБД обеспечивается при помощи метода connect класса DBI:
    $dbh = DBI->connect($dsn, $user, $auth, {options});
    
    $dbh    Это об'ект, при помощи его методов осуществляются взаимодействия с
            СУБД.
    $dsn    Строка, определяющая к какой базе данных подсоединятся и другие
            параметры. Зависит от DBD. На сегодняшний момент стандарта нет, но
            рекомендовано использовать стиль ODBC:
            'dbi:<имя DBD>:databasename=<название БД>;host=<Имя хоста>;port=<порт>';
    $user   Имя пользователя.
    
    $auth   Нечто, авторизующее пользователя. Обычно пароль.
    
    options Параметры DBI, передаются через анонимный хеш. В настоящее время
            понимаются три параметра: 
                    * RaiseError, если установлен, то при любой ошибке DBI убивает 
                    программу 
                    * PrintError, если установлен, то при ошибке DBI вызывает warn
                    * AutoCommit -- определяет порядок работы с транзакциями.
    
    Например:
    $dbh = DBI->connect('dbi:Pg:dbname=apavel', 'apavel', 'SomeSecret',
    {RaiseError=>1, AutoCommit=>0});
    
    Означает: Подсоедение к СУБД PostgreSQL, к базе данных apavel, с именем пользователя apavel и паролем. Все ошибки будут вызывать die, что удобно при отладке, а все изменения будут внесены только при подтверждении (commit) транзакций.

    Отсоединение обеспечивается при помощи метода disconnect: $dbh->disconnect();

    Механизм курсоров и подготовки запросов
    При работе с базами данных при помощи DBI используюся курсоры -- специальные об'екты, обеспечивающие последовательный доступ к результатам запросов. (В простейших случаях можно обойтись и без них, я расскажу об этом дальше.)
    Пример таблицы, используемой в дальнейшем:

    create table foo (
            bar varchar(50),
            baz int
    )       
    
    Получение данных
    $cursor = $dbh->prepare('select bar, baz from foo');
    # теперь $cursor -- курсор, и его необходимо исполнить
    $cursor->execute;
    # После исполнения запроса, результат можно получить из курсора при помощи
    # метода fetchrow_array
    while (($bar, $baz) = $cursor->fetchrow_array) {
            print "bar is: $bar, baz: $baz\n";
    }       
    
    Placeholders
    Очень часто бывает надо подготовить какой-либо запрос, а потом использовать его с разными значениями данных. DBI предлагает для механизм placeholders: В запросе на месте таких данных указываются вопросительные знаки, а сами значения передаются в метод execute() курсора. Например:
    $cursor = $dbh->prepare('select bar from foo where baz=?');
    $cursor->execute($baz);
    Особенно удобно это в случае вставки данных:
    $cursor = $dbh->prepare('insert into foo(bar, baz) values(?, ?)');
    while ( ... ) {
            $cursor->execute($bar, $baz);
    }
    
    Таким образом, СУБД разбирает запрос толлько один раз, а затем просто исполняет его, что экономит время. (Естественно, это верно только для DBMS с раздельными parse и execute, сейчас ни MySQL, ни PostgreSQL такое не поддерживают, поэтому их реализации DBD просто сохраняют запрос переданный $dbh->prepare() и затем подставляют в него данные при каждом $sth->execute().)

    Работа без курсоров.
    DBI предоставляет несколько методов для такого рода работы:
    Методы для запросов:
       * selectrow_array Возвращает одну строку запроса в виде массива,
       * selectall_arrayref Возвращает весь ответ сервера в виде массива ссылок на массивы. Методы для выражений, не возвращающих значений
       * do исполняет запрос
    Пример:

    #получить значение bar при baz=3
    ($bar) = $dbh->selectrow_array('select bar from foo where baz=3');
    
    # установить baz в некоторое значение при bar='somestring'
    $dbh->do("update set baz=1 where bar='somestring'");
    
    (Q 2.2) Как работать с записями кусочками: первые N записей, следующие N...?
    (A)Можно несколькими способами:
    1. Самый грубый и не эстетичный -- Просто прокручивая курсор:
            $c = $dbh->prepare('select baz, bar from foo');
            $c->execute;
            # если нужна последовательность с 26 по 50
    
            for ($k = 0; $k < 26; $k++) {
                    $c->fetchrow_array; 
            }
            # теперь можно вывести данные
            print "<table border=1><tr><th>bar</th><th>baz</th></tr>\n";
            while (($bar, $baz) = $c->fetchtrow_array) {
                    print "<tr><td>$bar</td><td>$baz</td></tr>\n";
            }
            $c->finish; # Закрыть курсор
            print "</table>";
    
    2. Используя курсоры СУБД
            # Показан ситаксис PostgeSQL
            $dbh->do('declare mycursor cursor for select bar, baz from foo');
            $dbh->do('move 25');
    
            # И теперь будем получать данные
            $c = $dbh->prepare('fetch forward 25 in mycursor');
            while (($bar, $baz) = $c->fetchrow_array) {
                    print ....;
            }
            $c->finish;
            $dbh->do('close mycursor');
    
    3. Для MySQL, авторы которого не в состоянии реализовать курсоры, можно использовать директиву LIMIT
            $c = $dbh->prepare('select bar, baz from foo limit 26,25');
            while (($bar, $baz) = $c->fetchrow_array) {
                    print ....;
            }
            $c->finish;
    
    (Q 2.2) Где взять документацию/учебник по SQL?
    (A) * В книжных магазинах сейчас есть неплохой набор книг по SQL. (От рекоммендаций воздержусь -- ни одну из них я не читал)
    * На MCP personal bookshelf (http://pbs.mcp.com) есть книга "Teach yourself SQL in 21 days" -- весьма рекомендую, очень хорошая книга.
    * На citforum (http://www.citforum.ru) были какие-то руководства
  •  Cборник часто задаваемых вопросов по языку PERL и программированию для WEB
    Лента новостей


    2006 (c) Copyright Hardline.ru