Владислав Гошко
В наше время базы данных широко используются в разных сферах человеческой
жизни. Самые простые базы данных – это просто файлы определенной структуры, а
сложные – это файлы, имеющие свой формат данных и, естественно, определенную
структуру. В данной статье я рассмотрю работу с базами данных на языке
программирования Perl. Для удобства пойдем от маленького к большому. Для начала
давайте рассмотрим обычный файл как базу данных, т.е. обычный файл можно
представить как определенно сформатированный текст. Файл будет отформатирован
следующим образом:
Name1|Value1#Value2#Value3#...#ValueN
Name2|Value1#Value2#Value3#...#ValueN
Name3|Value1#Value2#Value3#...#ValueN
...
NameN|Value1#Value2#Value3#...#ValueN
Почему разделителями я выбрал именно «|» и «#»?
Потому что они не являются безопасными символами (из RFC 2396). По той же
причине можно было выбрать разделителями любой символ, кроме латинских букв,
цифр, и вот этих:
";", "/",
"?", ":", "@", "&", "=",
"+", "$", ",", "-", "_",
".", "!", "~", "*", "'",
"(", ")"
Эти разделители (кроме вышеописанных символов)
могут использоваться в значениях или в именах переменных, только нужно будет
использовать модуль URI::Escape, для того чтобы переменные с этими
разделителями не испортили структуру. Специально для работы с нашими базами
данных создадим модуль simple_db.pm. Итак, начнем:
#!/usr/bin/perl
# создаем пакет
package simple_db;
use Exporter;
# как и говорилось,
используем модуль для перевода небезопасных символов в формат %XX
use URI::Escape;
@ISA=("Exporter");
@EXPORT=qw(&opendb &closedb
%g);
# экспортируем используемые
функции, а также хеш %g, для того чтобы функция знала,
# в какой файл записывать
изменения хеша, вводимого в аргументах
sub opendb{
my($h,$file)=@_;
my($name,$vals,@values);
local(*DB);
open(DB,$file) or return
0;
# блокируем файл на тот
случай, если во время чтения кто-то захочет изменить
# файл (совместная
блокировка – для чтения)
flock(DB,1);
while(<DB>){
chomp;
($name,$vals)=split(/\|/,$_,2);
next if !$name;
$name = uri_unescape($name);
@values = split(/#/,$vals);
# если значений
много, создаем анонимный массив, иначе просто
# присваиваем одно
значение
if($#values){
for(0..$#values){
#
переводим символы из %XX-формата
# в нормальный
вид
$values[$_]=uri_unescape
($values[$_]);
}
$$h{$name}=[@values];
}else{
$vals = uri_unescape($vals);
$$h{$name}=$vals;
}
}
close(DB);
# записываем в глобальный
хеш ассоциацию ссылки на хеш
# с открытым файлом
$g{$h}=$file;
}
sub сlosedb{
my($h)=@_;
my($key,$val,$fn);
# по имени хеша получаем
имя файла
$fn = $g{$h};
local(*DB);
# выходим из функции,
если файла не существует
return if !(-e $fn);
open(DB,">$fn")
or return 0;
# замыкаем файл
# (монопольная блокировка
– для записи)
flock(DB,2);
while(($key,$val) = each
%$h){
# обратно создаем
формат, переводя небезопасные
# символы в формат
%XX
print DB uri_escape($key)."|";
if(ref $val){
for(0..$#$val){
$$val[$_]=uri_escape($$val[$_]);
}
print DB join
"#",@$val;
}else{
$val=uri_escape($val);
print DB $val;
}
print DB "\n";
}
close(DB);
}
1;
Если вам нужно открывать файл с определенными
правами, то в функциях opendb() и closedb() нужно просто заменить open на sysopen
по следующему формату: sysopen FILEHANDLE, FILENAME, MODE, PERMS; и все.
Использование довольно простое, возьмем какой-нибудь файл test.pl. В этой же
директории должен лежать и модуль simple_db.pm. Вот test.pl:
#!/usr/bin/perl
use simple_db;
# открываем файл test.db и
ассоциируем с ним хеш %h,
# иначе умираем
opendb(\%h,"test.db")
or die $!;
# добавляем значения в хеш
$h{'supa|var'}=["special#","tes#t"];
# также легко можно добавить
массив
@tmp = ("array","tester");
# добавили ...
$h{'arr'}=\@tmp;
# или добавляем массив так
$h{'arr2'}=["some","vars"];
closedb(\%h) or die $!;
Данным образом будет создан файл test.db в
текущей директории. С переменной «supa|var» и значениями «speacial#» и «tes#t»,
и т. д. Я специально использовал небезопасные символы, а со второй и третьей
переменной все в порядке – там нет небезопасных символов...
А теперь давайте посмотрим, что записано в файле test.db:
supa%7Cvar|special%23#tes%23t
arr|array#tester
arr2|some#vars
Т.е. эти символы не помешали нашей структуре
файла, они всего лишь были переведены в %XX-формат. А теперь просмотрим всю
базу:
#!/usr/bin/perl
use simple_db;
opendb(\%h,"test.db")
or die $!;
while(($key,$val) = each %h){
print $key." =
";
if(ref $val){
# здесь, если
значение переменной ссылка на массив
print join "
; ",@$val;
}else{
print $val;
}
print "\n";
}
closedb(\%h) or die $!;
При выводе база будет выглядеть так:
supa|var
= special# ; tes#t
arr =
array ; tester
arr2 = some ; vars
Очищаем базу, просто очищая хеш, т.е. %h=(), или
можно просто удалить файл следующим образом:
unlink "test.db"
or die $!;
А при открытии базы открыть файл в режиме
создания, т.е. перед opendb() сделать следующее:
# $filename - в данном случае
имя нашего файла
if(!(-e "$filename")){
open(FILE,">$filename")
or die $!;
close(FILE);
}
Но эта база не может претендовать на большую
скорость и большие размеры и, как вы уже успели заметить, в ней не могут быть
созданы сложные структуры. Данная база данных вполне подойдет для небольших
объемов информации, например, для небольших сайтов или веб-сервисов. С помощью
классов можно создать свою вполне приличную базу данных. А теперь давайте расмотрим
другие базы данных. Начнем с портированной в вашу систему базу данных (их может
быть несколько). Функция dbmopen() довольна стара и поэтому позволяет
использовать лишь ту библиотеку DBM, c которой был построен Perl. Но по ходу
статьи рассмотрим и более новые методы.
Вот пример с dbmopen():
#!/usr/bin/perl
use Fcntl;
# специально для таких
переменных, как O_RDWR, O_CREAT
# O_RDWR - права: чтение,
запись
# O_CREAT - создать файл,
если он не существует
dbmopen(%HASH,$FILENAME,O_RDWR|O_CREAT,
0666) or die "Cant open $FILENAME: $!\n";
# заносим данные в базу
данных
$HASH{KEY}="VALUE";
# проверяем, существует ли
ключ
if(exists $HASH{KEY}){
# что-то делаем с
информацией, полученной из базы данных
$info = $HASH{KEY};
}
# удаляем какой-нибудь ключ
из базы
delete $HASH{SOME_KEY};
dbmclose(%HASH);
Также в начале программы можно добавить: use NDBM_File;
или use SDBM_File; или use GDBM_File; или use DB_File; – данные модули просто
переопределяют стандартный вариант, с которым был построен Perl. Есть также
другой способ открытия базы данных – tie и untie. Делается это так:
#!/usr/bin/perl
use DB_File;
# здесь это обязательно, т.к.
в функции tie() мы задаем модуль
use Fcntl;
tie(%HASH,"DB_File",$FILENAME,O_RDWR|O_CREAT,
0666) or die "Can't open $FILENAME: $!\n";
# все те же манипуляции с хешем,
которые описывались выше
# ...
# а потом
untie(%HASH);
Очищаются базы так же, как и в моем примере, т.к.
я свой пример делал по подобию этих баз. Но так же, как и мой пример, данные
базы не могут хранить сложные структуры. Для этого требуется модуль DB_File и
модуль MLDBM. Модуль MLDBM может хранить в хеше более сложные структуры, чем
просто числа и скаляры. Если его у ваc нет, то вы можете его скачать из
Интернета: запускаете программу ppm из дистрибутива Perl. И пишите: «install
MLDBM» – все должно пройти успешно. Использование вот такое:
#!/usr/bin/perl
use MLDBM 'DB_File';
use Fcntl; # для O_RDWR,
O_CREAT и т.д.
tie(%h, "MLDBM",
"glob.db", O_RDWR|O_CREAT, 0666) or die "Couldn't tie DB_File $users:
$!; aborting";
$usr{synthetic}->{password}="matrix
reloaded";
$h{users}=\%usr;
untie %h;
Таким образом была создана база данных glob.db, в
которой есть ключ «users», в котором есть пользователь «synthetic» с еще одним
вложенным хешем, в котором есть ключ «password» со значением «matrix reloaded».
Такая структура очень удобна и легко запрашиваема. Далее посмотрим, как мы
проверяем то, что создали:
#!/usr/bin/perl
use MLDBM 'DB_File';
use Fcntl; # для O_RDWR,
O_CREAT и т. д.
$access=0;
tie(%h,"MLDBM","glod.db",O_RDWR|O_CREAT,0666)
or die "Couldn't tie DB_File $users: $!; aborting";
while(($key,$val) = each %h){
if($key eq "users"){
if($val->{synthetic}->{password}
eq "matrix reloaded"){
$access=1;last;
# если все
правильно, завершаем цикл
}
}
}
untie(%h);
if($access){
print "Matrix has you...";
}else{
print "Follow the white
rabbit";
}
С удалением нужно немного по-другому, через временный
хеш:
tie(%h,"MLDBM","glob.db",O_RDWR|O_CREAT,0666)
or die "Couldn't tie DB_File $users: $!; aborting";
%tmp=%h;
delete $tmp{users}->{'somebody'};
%h=%tmp;
untie %h;
Добавлять тоже через временный хеш:
tie(%h,"MLDBM","glob.db",O_RDWR|O_CREAT,0666)
or die "Couldn't tie DB_File $users: $!; aborting";
%tmp=%h;
$tmp{users}->{'morpheus'}->{password}='zion';
%h=%tmp;
untie %h;
И в моем примере, и в других
базах используется работа с хешами, поэтому давайте рассмотрим несколько
примеров работы с хешами...
n Сортировка ключей хеша по алфавиту:
foreach $key (sort keys %unsorted){
$val = $unsorted{$key};
# здесь переборка ключей хеша
по алфавиту
# делаем что-то c $key и $val
}
n Сортировка по ассоциированным значениям:
foreach $key (sort {$unsorted{$a}
cmp $unsorted{$b} } keys %unsorted){
$val = $unsoreted{$key};
# что-то делаем с $key и $val...
}
n Сортировка по длине значений (почти также, как
и просто по значениям):
foreach $key (sort {length($unsorted{$a})
<=> length($unsorted{$b}) } keys %unsorted){
$val = $unsoreted{$key};
# что-то делаем с $key и $val...
}
Дальше рассмотрим небольшие примеры работы с
базами данных MySQL, при помощи модуля DBI и драйвера для работы с MySQL – DBD::mysql.
Эти модули также можно установить через ppm. А теперь посмотрим пример работы с
MySQL:
#!/usr/bin/perl
use DBI;
# настройки SQL сервера
$user = "synthetic"; #
логин и
$password = "test"; #
пароль для доступа к серверу
$host = "localhost"; #
адрес SQL-сервера
$db = "site"; #
база данных, с которой соединяемся
$port = 3306; #
порт (взят по умолчанию)
$driver = "mysql"; #
это драйвер для базы данных, т.е. вы можете указать драйвер своей
# базы и спокойно соединятся
с ней (естественно зная ее семантику)
# данные - просто для
проверки
$login = "Vlad";
$pass = "isitreal";
$conn = "DBI:$driver:database=$db;host=$host;port=$port";
# RaiseError => 1 -
сообщать об ошибках
$dbh = DBI->connect($conn,
$user, $password, {RaiseError => 1});
# задали название тэйбла -
для дальнейшего использования
$table = "users";
# создаем тэйбл "users"
$query = "CREATE TABLE
.$table(username char(16) not null,pass char(16) not null)";
# создали
$dbh->do($query);
# отсоединилсь
$dbh->disconnect();
Не особо сложными манипуляциями добавляем данные
в тэйбл:
# вся предыдущая
инициализация
$query = sprintf("INSERT
INTO .$table (username, pass) VALUES ('%s', '%s')", $login, $pass);
# выполнили...
$dbh->do($query);
# и т. д.
Выбираем из базы:
# вся предыдущая
инициализация.
# создаем запрос к базе и
выбираем все из тэйбла users
$sth = $dbh->prepare("select
* from .users");
# выполнили
$sth->execute();
while($row = $sth->fetchrow_arrayref()){
# в данном случае:
# $row->[0] - логин (username)
# $row->[1] - пароль (pass)
print $row->[0]."
".$row->[1];
}
# обязательно (!) говорим,
что завершили
$sth->finish();
Удаляем тэйбл, если он существует:
# вся предыдущая
инициализация
$query = "DROP TABLE IF
EXISTS .$table";
# выполнили...
$dbh->do($query);
# и т. д.
Вот, собственно, и все, в данной статье я не стал
особенно глубоко рассматривать работу с базами SQL, потому как это довольно
большая тема и требует отдельной статьи (если не книжки). Удачи в создании
систем управления базами данных!