DB_File
СОДЕРЖАНИЕ
- ИМЯ
- СИНОПСИС
- ОПИСАНИЕ
- DB_HASH
- DB_BTREE
- DB_RECNO
- ИНТЕРФЕЙС API
- ФИЛЬТРЫ DBM
- НАВОДКИ И СОВЕТЫ
- ЧАСТЫЕ ВОПРОСЫ
- СПИСОК ЛИТЕРАТУРЫ
- ИСТОРИЯ
- ОШИБКИ
- ДОСТУПНОСТЬ
- АВТОРСКИЕ ПРАВА
- СМОТРИТЕ ТАКЖЕ
- АВТОР
ИМЯ
DB_File — доступ к Berkeley DB версии 1.x из Perl5
СИНОПСИС
use DB_File;
[$X =] tie %hash, 'DB_File', [$filename, $flags, $mode, $DB_HASH] ;
[$X =] tie %hash, 'DB_File', $filename, $flags, $mode, $DB_BTREE ;
[$X =] tie @array, 'DB_File', $filename, $flags, $mode, $DB_RECNO ;
$status = $X->del($key [, $flags]) ;
$status = $X->put($key, $value [, $flags]) ;
$status = $X->get($key, $value [, $flags]) ;
$status = $X->seq($key, $value, $flags) ;
$status = $X->sync([$flags]) ;
$status = $X->fd ;
# BTREE only
$count = $X->get_dup($key) ;
@list = $X->get_dup($key) ;
%list = $X->get_dup($key, 1) ;
$status = $X->find_dup($key, $value) ;
$status = $X->del_dup($key, $value) ;
# RECNO only
$a = $X->length;
$a = $X->pop ;
$X->push(list);
$a = $X->shift;
$X->unshift(list);
@r = $X->splice(offset, length, elements);
# DBM Filters
$old_filter = $db->filter_store_key ( sub { ... } ) ;
$old_filter = $db->filter_store_value( sub { ... } ) ;
$old_filter = $db->filter_fetch_key ( sub { ... } ) ;
$old_filter = $db->filter_fetch_value( sub { ... } ) ;
untie %hash ;
untie @array ; ОПИСАНИЕ
DB_File — модуль, который позволяет Perl-программам использовать возможности Berkeley DB версии 1.x (если у вас более новая версия DB, см. Использование DB_File с Berkeley DB версии 2 или выше). Предполагается, что у вас под рукой есть справочные страницы Berkeley DB при чтении этой документации. Здесь определённый интерфейс тесно соответствует интерфейсу Berkeley DB.
Berkeley DB — библиотека C, которая предоставляет согласованный интерфейс к ряду форматов баз данных. DB_File предоставляет интерфейс ко всем трём типам баз данных, поддерживаемым в настоящее время Berkeley DB.
Типы файлов:
- DB_HASH
-
Этот тип базы данных позволяет хранить произвольные пары ключ/значение в файлах данных. Это эквивалентно функциональности, предоставляемой другими хэш-пакетами, такими как DBM, NDBM, ODBM, GDBM и SDBM. Однако помните, что файлы, созданные с помощью DB_HASH, несовместимы ни с одним из упомянутых пакетов.
В Berkeley DB встроен алгоритм хэширования по умолчанию, который будет достаточным для большинства приложений. Если вам действительно нужен свой алгоритм хэширования, вы можете написать его на Perl и заставить DB_File использовать его вместо него.
- DB_BTREE
-
Формат btree позволяет хранить произвольные пары ключ/значение в отсортированном, сбалансированном двоичном дереве.
Как и в формате DB_HASH, вы можете предоставить Perl-функцию для сравнения ключей. Однако по умолчанию ключи хранятся в лексикографическом порядке.
- DB_RECNO
-
DB_RECNO позволяет работать с файлами с фиксированной длиной и файлами с переменной длиной, обрабатывая их как пары ключ/значение, как в DB_HASH и DB_BTREE. В этом случае ключ будет состоять из номера записи (строки).
Использование DB_File с Berkeley DB версии 2 или выше
Хотя DB_File предназначен для использования с Berkeley DB версии 1, он также может использоваться с версиями 2, 3 или 4. В этом случае интерфейс ограничен функциональностью Berkeley DB 1.x. В местах, где интерфейс версии 2 или выше отличается, DB_File обеспечивает его работу, как в версии 1. Эта функция позволяет использовать скрипты DB_File, созданные с версией 1, с версиями 2 или выше без изменений.
Если вы хотите использовать новые возможности, доступные в Berkeley DB 2.x или выше, используйте вместо него Perl-модуль BerkeleyDB.
Примечание: Формат файла базы данных несколько раз изменялся в Berkeley DB версии 2, 3 и 4. Если вы не можете восстановить свои базы данных, вы должны выполнить дамп любых существующих баз данных с помощью утилиты db_dump или db_dump185, которая поставляется с Berkeley DB. После того как вы перестроили DB_File для использования Berkeley DB версии 2 или выше, ваши базы данных могут быть восстановлены с помощью db_load. Обратитесь к документации Berkeley DB для получения дополнительной информации.
Перед использованием версии 2.x или выше Berkeley DB с DB_File, пожалуйста, ознакомьтесь с АВТОРСКИМИ ПРАВАМИ.
Интерфейс к Berkeley DB
DB_File позволяет получить доступ к файлам Berkeley DB с помощью механизма tie() в Perl 5 (подробнее см. tie() в perlfunc). Эта функция позволяет DB_File получать доступ к файлам Berkeley DB, используя либо ассоциативный массив (для типов файлов DB_HASH и DB_BTREE), либо обычный массив (для типа файла DB_RECNO).
Помимо интерфейса tie(), вы также можете напрямую получить доступ к большинству функций, предоставляемых API Berkeley DB. См. ИНТЕРФЕЙС API.
Открытие файла базы данных Berkeley DB
Berkeley DB использует функцию dbopen() для открытия или создания базы данных. Вот прототип функции dbopen() на C:
DB*
dbopen (const char * file, int flags, int mode,
DBTYPE type, const void * openinfo) Параметр type — перечисление, которое указывает, какой из 3 методов интерфейса (DB_HASH, DB_BTREE или DB_RECNO) использовать. В зависимости от выбранного метода, конечный параметр openinfo указывает на структуру данных, которая позволяет настроить конкретный метод интерфейса.
В DB_File этот интерфейс обрабатывается немного по-другому. Вот эквивалентный вызов с использованием DB_File:
tie %array, 'DB_File', $filename, $flags, $mode, $DB_HASH ; Параметры filename, flags и mode — прямые аналоги соответствующих параметров dbopen(). Конечный параметр $DB_HASH выполняет функцию параметров type и openinfo в dbopen().
В приведённом выше примере $DB_HASH — предварительно определённая ссылка на объект-хэш. В DB_File существуют три такие ссылки: $DB_HASH, $DB_BTREE и $DB_RECNO.
Разрешенные ключи в каждой из этих предварительно определённых ссылок ограничены именами, используемыми в эквивалентной структуре C. Например, ссылка $DB_HASH будет разрешать только ключи, называемые bsize, cachesize, ffactor, hash, lorder и nelem.
Чтобы изменить один из этих элементов, просто присвойте ему новое значение, как в этом примере:
$DB_HASH->{'cachesize'} = 10000 ; Три предварительно определённые переменные $DB_HASH, $DB_BTREE и $DB_RECNO обычно подходят для большинства приложений. Если вам нужно создать дополнительные экземпляры этих объектов, для каждого типа файла доступны конструкторы.
Вот примеры конструкторов и допустимые параметры для DB_HASH, DB_BTREE и DB_RECNO соответственно.
$a = new DB_File::HASHINFO ;
$a->{'bsize'} ;
$a->{'cachesize'} ;
$a->{'ffactor'};
$a->{'hash'} ;
$a->{'lorder'} ;
$a->{'nelem'} ;
$b = new DB_File::BTREEINFO ;
$b->{'flags'} ;
$b->{'cachesize'} ;
$b->{'maxkeypage'} ;
$b->{'minkeypage'} ;
$b->{'psize'} ;
$b->{'compare'} ;
$b->{'prefix'} ;
$b->{'lorder'} ;
$c = new DB_File::RECNOINFO ;
$c->{'bval'} ;
$c->{'cachesize'} ;
$c->{'psize'} ;
$c->{'flags'} ;
$c->{'lorder'} ;
$c->{'reclen'} ;
$c->{'bfname'} ; Значения, хранящиеся в вышеуказанных хешах, в основном являются прямыми аналогами своих C-аналогов. Как и их C-аналоги, все они установлены по умолчанию — это значит, что вам не нужно устанавливать все значения, если вы хотите изменить только одно. Вот пример:
$a = new DB_File::HASHINFO ;
$a->{'cachesize'} = 12345 ;
tie %y, 'DB_File', "filename", $flags, 0777, $a ; Некоторые параметры требуют дополнительного обсуждения. При использовании C-аналоги ключей hash, compare и prefix хранят указатели на C-функции. В DB_File эти ключи используются для хранения ссылок на Perl-подпрограммы. Ниже приведены шаблоны для каждой из подпрограмм:
sub hash
{
my ($data) = @_ ;
...
# return the hash value for $data
return $hash ;
}
sub compare
{
my ($key, $key2) = @_ ;
...
# return 0 if $key1 eq $key2
# -1 if $key1 lt $key2
# 1 if $key1 gt $key2
return (-1 , 0 or 1) ;
}
sub prefix
{
my ($key, $key2) = @_ ;
...
# return number of bytes of $key2 which are
# necessary to determine that it is greater than $key1
return $bytes ;
} См. Изменение порядка сортировки BTREE для примера использования шаблона compare.
Если вы используете интерфейс DB_RECNO и планируете использовать bval, ознакомьтесь с Параметром 'bval'.
Параметры по умолчанию
Можно опустить некоторые или все последние 4 параметра в вызове tie и позволить им принять значения по умолчанию. Поскольку DB_HASH является наиболее распространенным форматом файлов, вызов:
tie %A, "DB_File", "filename" ; эквивалентен:
tie %A, "DB_File", "filename", O_CREAT|O_RDWR, 0666, $DB_HASH ; Также можно опустить параметр имени файла, поэтому вызов:
tie %A, "DB_File" ; эквивалентен:
tie %A, "DB_File", undef, O_CREAT|O_RDWR, 0666, $DB_HASH ; См. «Базы данных в памяти» для обсуждения использования undef вместо имени файла.
Базы данных в памяти
Berkeley DB позволяет создавать базы данных в памяти, используя NULL (то есть, (char *)0 в C) вместо имени файла. DB_File использует undef вместо NULL для обеспечения этой функциональности.
DB_HASH
Формат файла DB_HASH, вероятно, является наиболее часто используемым из трех форматов файлов, которые поддерживает DB_File. Он также очень прост в использовании.
Простой пример
Этот пример показывает, как создать базу данных, добавить пары ключ/значение в базу данных, удалить пары ключ/значение и, наконец, как перечислить содержимое базы данных.
use warnings ;
use strict ;
use DB_File ;
our (%h, $k, $v) ;
unlink "fruit" ;
tie %h, "DB_File", "fruit", O_RDWR|O_CREAT, 0666, $DB_HASH
or die "Cannot open file 'fruit': $!\n";
# Add a few key/value pairs to the file
$h{"apple"} = "red" ;
$h{"orange"} = "orange" ;
$h{"banana"} = "yellow" ;
$h{"tomato"} = "red" ;
# Check for existence of a key
print "Banana Exists\n\n" if $h{"banana"} ;
# Delete a key/value pair.
delete $h{"apple"} ;
# print the contents of the file
while (($k, $v) = each %h)
{ print "$k -> $v\n" }
untie %h ; вот вывод:
Banana Exists
orange -> orange
tomato -> red
banana -> yellow Обратите внимание, что, как и обычные ассоциативные массивы, порядок извлеченных ключей имеет, по-видимому, случайный порядок.
DB_BTREE
Формат DB_BTREE полезен, когда вы хотите хранить данные в определенном порядке. По умолчанию ключи будут храниться в лексикографическом порядке, но, как вы увидите из примера, показанного в следующем разделе, очень легко определить свою собственную функцию сортировки.
Изменение порядка сортировки BTREE
Этот скрипт показывает, как переопределить стандартный алгоритм сортировки, используемый BTREE. Вместо использования обычного лексикографического порядка будет использоваться функция сравнения, не учитывающая регистр.
use warnings ;
use strict ;
use DB_File ;
my %h ;
sub Compare
{
my ($key1, $key2) = @_ ;
"\L$key1" cmp "\L$key2" ;
}
# specify the Perl sub that will do the comparison
$DB_BTREE->{'compare'} = \&Compare ;
unlink "tree" ;
tie %h, "DB_File", "tree", O_RDWR|O_CREAT, 0666, $DB_BTREE
or die "Cannot open file 'tree': $!\n" ;
# Add a key/value pair to the file
$h{'Wall'} = 'Larry' ;
$h{'Smith'} = 'John' ;
$h{'mouse'} = 'mickey' ;
$h{'duck'} = 'donald' ;
# Delete
delete $h{"duck"} ;
# Cycle through the keys printing them in order.
# Note it is not necessary to sort the keys as
# the btree will have kept them in order automatically.
foreach (keys %h)
{ print "$_\n" }
untie %h ; Вот вывод из приведенного выше кода.
mouse
Smith
Wall Есть несколько моментов, которые следует учитывать, если вы хотите изменить порядок в базе данных BTREE:
-
Новая функция сравнения должна быть указана при создании базы данных.
-
Вы не можете изменить порядок после создания базы данных. Таким образом, вы должны использовать ту же функцию сравнения каждый раз при обращении к базе данных.
-
Дублирование ключей полностью определяется функцией сравнения. В примере с игнорированием регистра ключи: «KEY» и «key» будут считаться дубликатами, и присвоение второму перекроет первый. Если дубликаты разрешены (с флагом R_DUP, обсуждаемым ниже), в базе данных хранится только одна копия дублирующих ключей — так (еще раз с приведенным выше примером) присвоение трех значений ключам: «KEY», «Key» и «key» оставит только первый ключ: «KEY» в базе данных со тремя значениями. В некоторых ситуациях это приводит к потере информации, поэтому необходимо проявлять осторожность, предоставляя полностью определенные функции сравнения при необходимости. Например, приведенная выше функция сравнения может быть модифицирована для дополнительного сравнения по регистру, если два ключа равны в сравнении, не учитывающем регистр:
sub compare { my($key1, $key2) = @_; lc $key1 cmp lc $key2 || $key1 cmp $key2; }И теперь дубликаты будут возникать только тогда, когда сами ключи действительно одинаковы. (примечание: в версиях библиотеки db до примерно ноября 1996 года такие дубликаты ключей сохранялись, поэтому было возможно восстановить исходные ключи в наборах ключей, которые сравнивались как равные).
Обработка дубликатов ключей
Тип файла BTREE необязательно позволяет связывать один ключ с произвольным количеством значений. Этот параметр активируется путем установки элемента флагов в $DB_BTREE в R_DUP при создании базы данных.
Возникают некоторые трудности при использовании интерфейса связанных хешей, если вы хотите управлять базой данных BTREE с дублирующими ключами. Рассмотрим этот код:
use warnings ;
use strict ;
use DB_File ;
my ($filename, %h) ;
$filename = "tree" ;
unlink $filename ;
# Enable duplicate records
$DB_BTREE->{'flags'} = R_DUP ;
tie %h, "DB_File", $filename, O_RDWR|O_CREAT, 0666, $DB_BTREE
or die "Cannot open $filename: $!\n";
# Add some key/value pairs to the file
$h{'Wall'} = 'Larry' ;
$h{'Wall'} = 'Brick' ; # Note the duplicate key
$h{'Wall'} = 'Brick' ; # Note the duplicate key and value
$h{'Smith'} = 'John' ;
$h{'mouse'} = 'mickey' ;
# iterate through the associative array
# and print each key/value pair.
foreach (sort keys %h)
{ print "$_ -> $h{$_}\n" }
untie %h ; Вот вывод:
Smith -> John
Wall -> Larry
Wall -> Larry
Wall -> Larry
mouse -> mickey Как вы можете видеть, 3 записи были успешно созданы с ключом Wall — единственное, что, когда они извлекаются из базы данных, они, похоже, имеют одинаковое значение, а именно Larry. Проблема вызвана способом работы интерфейса ассоциативного массива. В сущности, когда интерфейс ассоциативного массива используется для извлечения значения, связанного с заданным ключом, он всегда извлекает только первое значение.
Хотя из приведенного выше кода это может не быть сразу очевидно, интерфейс ассоциативного массива можно использовать для записи значений с дублирующими ключами, но нельзя использовать его для чтения их из базы данных.
Способ решения этой проблемы заключается в использовании метода API Berkeley DB, называемого seq. Этот метод позволяет последовательно обращаться к парам ключ/значение. См. «ИНТЕРФЕЙС API» для получения подробностей о методе seq и API в целом.
Вот приведенный выше скрипт, переписанный с использованием метода API seq.
use warnings ;
use strict ;
use DB_File ;
my ($filename, $x, %h, $status, $key, $value) ;
$filename = "tree" ;
unlink $filename ;
# Enable duplicate records
$DB_BTREE->{'flags'} = R_DUP ;
$x = tie %h, "DB_File", $filename, O_RDWR|O_CREAT, 0666, $DB_BTREE
or die "Cannot open $filename: $!\n";
# Add some key/value pairs to the file
$h{'Wall'} = 'Larry' ;
$h{'Wall'} = 'Brick' ; # Note the duplicate key
$h{'Wall'} = 'Brick' ; # Note the duplicate key and value
$h{'Smith'} = 'John' ;
$h{'mouse'} = 'mickey' ;
# iterate through the btree using seq
# and print each key/value pair.
$key = $value = 0 ;
for ($status = $x->seq($key, $value, R_FIRST) ;
$status == 0 ;
$status = $x->seq($key, $value, R_NEXT) )
{ print "$key -> $value\n" }
undef $x ;
untie %h ; что выводит:
Smith -> John
Wall -> Brick
Wall -> Brick
Wall -> Larry
mouse -> mickey На этот раз мы получили все пары ключ/значение, включая несколько значений, связанных с ключом Wall.
Для облегчения работы с дублирующими ключами, DB_File поставляется с несколькими служебными методами.
Метод get_dup()
Метод get_dup помогает в чтении дублирующих значений из баз данных BTREE. Метод может принимать следующие формы:
$count = $x->get_dup($key) ;
@list = $x->get_dup($key) ;
%list = $x->get_dup($key, 1) ; В скалярном контексте метод возвращает количество значений, связанных с ключом, $key.
В списочном контексте он возвращает все значения, соответствующие $key. Обратите внимание, что значения будут возвращены в, по-видимому, случайном порядке.
В списочном контексте, если второй параметр присутствует и оценивается как TRUE, метод возвращает ассоциативный массив. Ключи ассоциативного массива соответствуют значениям, которые совпали в BTREE, а значения массива — это количество раз, когда конкретное значение встречалось в BTREE.
Итак, предположим, что база данных, созданная выше, может использоваться get_dup следующим образом:
use warnings ;
use strict ;
use DB_File ;
my ($filename, $x, %h) ;
$filename = "tree" ;
# Enable duplicate records
$DB_BTREE->{'flags'} = R_DUP ;
$x = tie %h, "DB_File", $filename, O_RDWR|O_CREAT, 0666, $DB_BTREE
or die "Cannot open $filename: $!\n";
my $cnt = $x->get_dup("Wall") ;
print "Wall occurred $cnt times\n" ;
my %hash = $x->get_dup("Wall", 1) ;
print "Larry is there\n" if $hash{'Larry'} ;
print "There are $hash{'Brick'} Brick Walls\n" ;
my @list = sort $x->get_dup("Wall") ;
print "Wall => [@list]\n" ;
@list = $x->get_dup("Smith") ;
print "Smith => [@list]\n" ;
@list = $x->get_dup("Dog") ;
print "Dog => [@list]\n" ; и выведет:
Wall occurred 3 times
Larry is there
There are 2 Brick Walls
Wall => [Brick Brick Larry]
Smith => [John]
Dog => [] Метод find_dup()
$status = $X->find_dup($key, $value) ; Этот метод проверяет существование определенной пары ключ/значение. Если пара существует, указатель оставляется, указывая на пару, и метод возвращает 0. В противном случае метод возвращает ненулевое значение.
Предположим, база данных из предыдущего примера:
use warnings ;
use strict ;
use DB_File ;
my ($filename, $x, %h, $found) ;
$filename = "tree" ;
# Enable duplicate records
$DB_BTREE->{'flags'} = R_DUP ;
$x = tie %h, "DB_File", $filename, O_RDWR|O_CREAT, 0666, $DB_BTREE
or die "Cannot open $filename: $!\n";
$found = ( $x->find_dup("Wall", "Larry") == 0 ? "" : "not") ;
print "Larry Wall is $found there\n" ;
$found = ( $x->find_dup("Wall", "Harry") == 0 ? "" : "not") ;
print "Harry Wall is $found there\n" ;
undef $x ;
untie %h ; выводит это
Larry Wall is there
Harry Wall is not there Метод del_dup()
$status = $X->del_dup($key, $value) ; Этот метод удаляет определенную пару ключ/значение. Он возвращает 0, если они существуют и были успешно удалены. В противном случае метод возвращает ненулевое значение.
Опять же, предполагая существование базы данных tree
use warnings ;
use strict ;
use DB_File ;
my ($filename, $x, %h, $found) ;
$filename = "tree" ;
# Enable duplicate records
$DB_BTREE->{'flags'} = R_DUP ;
$x = tie %h, "DB_File", $filename, O_RDWR|O_CREAT, 0666, $DB_BTREE
or die "Cannot open $filename: $!\n";
$x->del_dup("Wall", "Larry") ;
$found = ( $x->find_dup("Wall", "Larry") == 0 ? "" : "not") ;
print "Larry Wall is $found there\n" ;
undef $x ;
untie %h ; выводит это
Larry Wall is not there Сопоставление частичных ключей
Интерфейс BTREE имеет функцию, позволяющую сопоставлять частичные ключи. Эта функциональность только доступна при использовании метода seq вместе с флагом R_CURSOR.
$x->seq($key, $value, R_CURSOR) ; Вот соответствующая цитата из руководства dbopen, где определено использование флага R_CURSOR с seq:
Note, for the DB_BTREE access method, the returned key is not
necessarily an exact match for the specified key. The returned key
is the smallest key greater than or equal to the specified key,
permitting partial key matches and range searches. В примере скрипта ниже подпрограмма match использует эту функцию для поиска и вывода первой соответствующей пары ключ/значение при заданном частичном ключе.
use warnings ;
use strict ;
use DB_File ;
use Fcntl ;
my ($filename, $x, %h, $st, $key, $value) ;
sub match
{
my $key = shift ;
my $value = 0;
my $orig_key = $key ;
$x->seq($key, $value, R_CURSOR) ;
print "$orig_key\t-> $key\t-> $value\n" ;
}
$filename = "tree" ;
unlink $filename ;
$x = tie %h, "DB_File", $filename, O_RDWR|O_CREAT, 0666, $DB_BTREE
or die "Cannot open $filename: $!\n";
# Add some key/value pairs to the file
$h{'mouse'} = 'mickey' ;
$h{'Wall'} = 'Larry' ;
$h{'Walls'} = 'Brick' ;
$h{'Smith'} = 'John' ;
$key = $value = 0 ;
print "IN ORDER\n" ;
for ($st = $x->seq($key, $value, R_FIRST) ;
$st == 0 ;
$st = $x->seq($key, $value, R_NEXT) )
{ print "$key -> $value\n" }
print "\nPARTIAL MATCH\n" ;
match "Wa" ;
match "A" ;
match "a" ;
undef $x ;
untie %h ; Вот вывод:
IN ORDER
Smith -> John
Wall -> Larry
Walls -> Brick
mouse -> mickey
PARTIAL MATCH
Wa -> Wall -> Larry
A -> Smith -> John
a -> mouse -> mickey DB_RECNO
DB_RECNO предоставляет интерфейс к плоским текстовым файлам. Поддерживаются как переменные, так и фиксированные длины записей.
Для повышения совместимости RECNO с Perl, смещение массива для всех массивов RECNO начинается с 0, а не с 1, как в Berkeley DB.
Как и в обычных перловских массивах, к массиву RECNO можно обращаться с помощью отрицательных индексов. Индекс -1 относится к последнему элементу массива, -2 ко второму последнему и так далее. Попытка получить доступ к элементу до начала массива приведет к ошибке выполнения.
Параметр 'bval'
Работа параметра bval заслуживает обсуждения. Вот определение bval из руководства по recno Berkeley DB 1.85:
The delimiting byte to be used to mark the end of a
record for variable-length records, and the pad charac-
ter for fixed-length records. If no value is speci-
fied, newlines (``\n'') are used to mark the end of
variable-length records and fixed-length records are
padded with spaces. Второе предложение неправильное. На самом деле bval будет по умолчанию устанавливаться в "\n" только когда параметр openinfo в dbopen равен NULL. Если используется ненулевой параметр openinfo, то будет использоваться значение, которое случайно находится в bval. Это означает, что вы всегда должны указывать bval при использовании любого из параметров в параметре openinfo. Эта ошибка в документации будет исправлена в следующем выпуске Berkeley DB.
Это проясняет ситуацию в отношении самого Berkeley DB. Что насчет DB_File? Ну, поведение, определенное в цитате выше, довольно полезно, поэтому DB_File соответствует ему.
Это означает, что вы можете указать другие параметры (например, cachesize) и все равно иметь bval по умолчанию "\n" для записей переменной длины и места для записей фиксированной длины.
Обратите также внимание, что параметр bval позволяет указывать только один байт в качестве разделителя.
Простой пример
Вот простой пример, использующий RECNO (если вы используете версию Perl ранее, чем 5.004_57, этот пример не будет работать — см. «Дополнительные методы RECNO» для обходного решения).
use warnings ;
use strict ;
use DB_File ;
my $filename = "text" ;
unlink $filename ;
my @h ;
tie @h, "DB_File", $filename, O_RDWR|O_CREAT, 0666, $DB_RECNO
or die "Cannot open file 'text': $!\n" ;
# Add a few key/value pairs to the file
$h[0] = "orange" ;
$h[1] = "blue" ;
$h[2] = "yellow" ;
push @h, "green", "black" ;
my $elements = scalar @h ;
print "The array contains $elements entries\n" ;
my $last = pop @h ;
print "popped $last\n" ;
unshift @h, "white" ;
my $first = shift @h ;
print "shifted $first\n" ;
# Check for existence of a key
print "Element 1 Exists with value $h[1]\n" if $h[1] ;
# use a negative index
print "The last element is $h[-1]\n" ;
print "The 2nd last element is $h[-2]\n" ;
untie @h ; Вот вывод из скрипта:
The array contains 5 entries
popped black
shifted white
Element 1 Exists with value blue
The last element is green
The 2nd last element is yellow Дополнительные методы RECNO
Если вы используете версию Perl ранее, чем 5.004_57, интерфейс связанного массива довольно ограничен. В приведенном выше примере скрипта push, pop, shift, unshift или определение длины массива не будет работать со связанным массивом.
Чтобы сделать интерфейс более полезным для более старых версий Perl, с DB_File предоставляется ряд методов для имитации отсутствующих операций над массивами. Ко всем этим методам можно получить доступ через объект, возвращаемый из вызова tie.
Вот эти методы:
- $X->push(list) ;
-
Добавляет элементы
listв конец массива. - $value = $X->pop ;
-
Удаляет и возвращает последний элемент массива.
- $X->shift
-
Удаляет и возвращает первый элемент массива.
- $X->unshift(list) ;
-
Добавляет элементы
listв начало массива. - $X->length
-
Возвращает количество элементов в массиве.
- $X->splice(offset, length, elements);
-
Возвращает срез массива.
Другой пример
Вот более полный пример, использующий некоторые из описанных выше методов. Он также использует интерфейс API напрямую (см. «ИНТЕРФЕЙС API»).
use warnings ;
use strict ;
my (@h, $H, $file, $i) ;
use DB_File ;
use Fcntl ;
$file = "text" ;
unlink $file ;
$H = tie @h, "DB_File", $file, O_RDWR|O_CREAT, 0666, $DB_RECNO
or die "Cannot open file $file: $!\n" ;
# first create a text file to play with
$h[0] = "zero" ;
$h[1] = "one" ;
$h[2] = "two" ;
$h[3] = "three" ;
$h[4] = "four" ;
# Print the records in order.
#
# The length method is needed here because evaluating a tied
# array in a scalar context does not return the number of
# elements in the array.
print "\nORIGINAL\n" ;
foreach $i (0 .. $H->length - 1) {
print "$i: $h[$i]\n" ;
}
# use the push & pop methods
$a = $H->pop ;
$H->push("last") ;
print "\nThe last record was [$a]\n" ;
# and the shift & unshift methods
$a = $H->shift ;
$H->unshift("first") ;
print "The first record was [$a]\n" ;
# Use the API to add a new record after record 2.
$i = 2 ;
$H->put($i, "Newbie", R_IAFTER) ;
# and a new record before record 1.
$i = 1 ;
$H->put($i, "New One", R_IBEFORE) ;
# delete record 3
$H->del(3) ;
# now print the records in reverse order
print "\nREVERSE\n" ;
for ($i = $H->length - 1 ; $i >= 0 ; -- $i)
{ print "$i: $h[$i]\n" }
# same again, but use the API functions instead
print "\nREVERSE again\n" ;
my ($s, $k, $v) = (0, 0, 0) ;
for ($s = $H->seq($k, $v, R_LAST) ;
$s == 0 ;
$s = $H->seq($k, $v, R_PREV))
{ print "$k: $v\n" }
undef $H ;
untie @h ; и вот что он выводит:
ORIGINAL
0: zero
1: one
2: two
3: three
4: four
The last record was [four]
The first record was [zero]
REVERSE
5: last
4: three
3: Newbie
2: one
1: New One
0: first
REVERSE again
5: last
4: three
3: Newbie
2: one
1: New One
0: first Примечания:
-
Вместо итерации по массиву,
@hвот так:foreach $i (@h)необходимо использовать либо это:
foreach $i (0 .. $H->length - 1)или это:
for ($a = $H->get($k, $v, R_FIRST) ; $a == 0 ; $a = $H->get($k, $v, R_NEXT) ) -
Обратите внимание, что оба раза при использовании метода
putиндекс записи указывался с помощью переменной,$i, а не буквального значения. Это происходит потому, чтоputвернёт номер записи вставленной строки через этот параметр.
Интерфейс API
Помимо доступа к Berkeley DB с помощью связанного хэша или массива, также можно напрямую использовать большинство функций API, определённых в документации Berkeley DB.
Для этого необходимо сохранить копию объекта, возвращаемого из связки.
$db = tie %hash, "DB_File", "filename" ; После этого вы можете получить доступ к функциям API Berkeley DB как к методам DB_File напрямую, например так:
$db->put($key, $value, R_NOOVERWRITE) ; Важно: Если вы сохранили копию объекта, возвращаемого из tie, файл базы данных, лежащий в основе, не будет закрыт, пока переменная связки не будет разорвана и все копии сохранённого объекта не будут уничтожены.
use DB_File ;
$db = tie %hash, "DB_File", "filename"
or die "Cannot tie filename: $!" ;
...
undef $db ;
untie %hash ; См. "The untie() Gotcha" для получения дополнительной информации.
Доступны все функции, определённые в dbopen, за исключением close() и самого dbopen(). Интерфейс методов DB_File для поддерживаемых функций был реализован для отражения работы Berkeley DB, насколько это возможно. В частности, обратите внимание на то, что:
-
Методы возвращают значение состояния. Все методы возвращают 0 при успехе. Все возвращают -1 для обозначения ошибки и устанавливают
$!в точный код ошибки. Код возврата 1, как правило (но не всегда), означает, что указанный ключ не существовал в базе данных.Определены и другие коды возврата. См. ниже и в документации Berkeley DB для получения подробной информации. Документация Berkeley DB должна быть использована в качестве окончательного источника.
-
Всякий раз, когда функция Berkeley DB возвращает данные через один из своих параметров, эквивалентный метод DB_File делает ровно то же самое.
-
Если вы внимательны, можно смешивать вызовы API с интерфейсом связанного хэша/массива в одной части кода. Хотя лишь несколько методов, используемых для реализации связанного интерфейса, в настоящее время используют курсор, вы всегда должны предполагать, что курсор изменился всякий раз, когда используется связанный интерфейс хэша/массива. Например, этот код, вероятно, не сделает того, что вы ожидаете:
$X = tie %x, 'DB_File', $filename, O_RDWR|O_CREAT, 0777, $DB_BTREE or die "Cannot tie $filename: $!" ; # Get the first key/value pair and set the cursor $X->seq($key, $value, R_FIRST) ; # this line will modify the cursor $count = scalar keys %x ; # Get the second key/value pair. # oops, it didn't, it got the last key/value pair! $X->seq($key, $value, R_NEXT) ;Код выше можно переупорядочить, чтобы обойти проблему, например так:
$X = tie %x, 'DB_File', $filename, O_RDWR|O_CREAT, 0777, $DB_BTREE or die "Cannot tie $filename: $!" ; # this line will modify the cursor $count = scalar keys %x ; # Get the first key/value pair and set the cursor $X->seq($key, $value, R_FIRST) ; # Get the second key/value pair. # worked this time. $X->seq($key, $value, R_NEXT) ;
Все константы, определённые в dbopen для использования в параметрах флагов в методах, определённых ниже, также доступны. Обратитесь к документации Berkeley DB для точного значения значений флагов.
Ниже приведён список доступных методов.
- $status = $X->get($key, $value [, $flags]) ;
-
Используя ключ (
$key), этот метод считывает связанное с ним значение из базы данных. Прочитанное из базы данных значение возвращается в параметре$value.Если ключ не существует, метод возвращает 1.
В настоящее время для этого метода не определены флаги.
- $status = $X->put($key, $value [, $flags]) ;
-
Сохраняет пару ключ/значение в базе данных.
Если вы используете флаги R_IAFTER или R_IBEFORE, параметр
$keyбудет содержать номер записи вставленной пары ключ/значение.Допустимые флаги: R_CURSOR, R_IAFTER, R_IBEFORE, R_NOOVERWRITE и R_SETCURSOR.
- $status = $X->del($key [, $flags]) ;
-
Удаляет все пары ключ/значение с ключом
$keyиз базы данных.Код возврата 1 означает, что запрашиваемый ключ отсутствовал в базе данных.
В настоящее время единственный допустимый флаг — R_CURSOR.
- $status = $X->fd ;
-
Возвращает дескриптор файла для базовой базы данных.
См. "Locking: The Trouble with fd" для объяснения, почему вы не должны использовать
fdдля блокировки вашей базы данных. - $status = $X->seq($key, $value, $flags) ;
-
Этот интерфейс позволяет последовательно извлекать данные из базы данных. Полные сведения см. в dbopen.
Оба параметра
$keyи$valueбудут установлены в пару ключ/значение, прочитанную из базы данных.Параметр флагов является обязательным. Допустимые значения флагов: R_CURSOR, R_FIRST, R_LAST, R_NEXT и R_PREV.
- $status = $X->sync([$flags]) ;
-
Очищает любые кэшированные буферы на диск.
В настоящее время единственный допустимый флаг — R_RECNOSYNC.
Фильтры DBM
Фильтр DBM — это фрагмент кода, который используется всякий раз, когда вы хотите применить одно и то же преобразование ко всем ключам и/или значениям в базе данных DBM. Примером является кодирование данных в UTF-8 перед записью в базу данных и декодирование UTF-8 при чтении из файла базы данных.
Существует два способа использования фильтра DBM.
-
Используя низкоуровневый API, определённый ниже.
-
Используя модуль DBM_Filter. Этот модуль скрывает сложность API, определённого ниже, и поставляется с несколькими "готовыми" фильтрами, охватывающими некоторые распространённые случаи использования.
Рекомендуется использовать модуль DBM_Filter.
Низкоуровневый API фильтра DBM
Фильтры DBM связаны четырьмя методами. Все они работают одинаково, и каждый из них используется для установки (или отключения) одного фильтра DBM. Каждый из них принимает один параметр — ссылку на подпрограмму. Единственное отличие между ними — место установки фильтра.
Вкратце:
- filter_store_key
-
Если фильтр установлен с помощью этого метода, он будет вызываться каждый раз, когда вы записываете ключ в базу данных DBM.
- filter_store_value
-
Если фильтр установлен с помощью этого метода, он будет вызываться каждый раз, когда вы записываете значение в базу данных DBM.
- filter_fetch_key
-
Если фильтр установлен с помощью этого метода, он будет вызываться каждый раз, когда вы считываете ключ из базы данных DBM.
- filter_fetch_value
-
Если фильтр установлен с помощью этого метода, он будет вызываться каждый раз, когда вы считываете значение из базы данных DBM.
Вы можете использовать любое сочетание методов, от нуля до четырёх.
Все методы фильтра возвращают существующий фильтр, если он есть, или undef в противном случае.
Для удаления фильтра передайте undef в него.
Фильтр
Когда Perl вызывает каждый фильтр, локальная копия $_ будет содержать ключ или значение, подлежащие фильтрации. Фильтрация достигается путём изменения содержимого $_. Код возврата фильтра игнорируется.
Пример — проблема нуль-терминации.
Рассмотрим следующий сценарий. У вас есть база данных DBM, которую необходимо совместно использовать с приложением на C от третьей стороны. Приложение на C предполагает, что все ключи и значения имеют нуль-терминацию. К сожалению, когда Perl записывает в базы данных DBM, он не использует нуль-терминацию, поэтому ваше приложение Perl должно самостоятельно обрабатывать нуль-терминацию. При записи в базу данных вам нужно использовать что-то вроде этого:
$hash{"$key\0"} = "$value\0" ; Аналогично, нуль необходимо учитывать при расчёте длины существующих ключей/значений.
Гораздо лучше было бы игнорировать проблему нуль-терминации в основном коде приложения и иметь механизм, который автоматически добавлял бы завершающий нуль ко всем ключам и значениям при записи в базу данных и удалял бы его при чтении из базы данных. Как вы, наверное, уже догадались, эту проблему очень легко решить с помощью фильтров DBM.
use warnings ;
use strict ;
use DB_File ;
my %hash ;
my $filename = "filt" ;
unlink $filename ;
my $db = tie %hash, 'DB_File', $filename, O_CREAT|O_RDWR, 0666, $DB_HASH
or die "Cannot open $filename: $!\n" ;
# Install DBM Filters
$db->filter_fetch_key ( sub { s/\0$// } ) ;
$db->filter_store_key ( sub { $_ .= "\0" } ) ;
$db->filter_fetch_value( sub { s/\0$// } ) ;
$db->filter_store_value( sub { $_ .= "\0" } ) ;
$hash{"abc"} = "def" ;
my $a = $hash{"ABC"} ;
# ...
undef $db ;
untie %hash ; Надеюсь, содержимое каждого фильтра должно быть самоочевидным. Оба фильтра "чтения" удаляют завершающий нуль, а оба фильтра "записи" добавляют завершающий нуль.
Другой пример — ключ — целое число C.
Вот ещё один реальный пример. По умолчанию, когда Perl записывает в базу данных DBM, он всегда записывает ключ и значение как строки. Итак, когда вы используете это:
$hash{12345} = "something" ; ключ 12345 будет сохранён в базе данных DBM как строка длиной 5 байтов "12345". Если вы хотите, чтобы ключ хранился в базе данных DBM как целое число C, вам нужно использовать pack при записи и unpack при чтении.
Вот фильтр DBM, который это делает:
use warnings ;
use strict ;
use DB_File ;
my %hash ;
my $filename = "filt" ;
unlink $filename ;
my $db = tie %hash, 'DB_File', $filename, O_CREAT|O_RDWR, 0666, $DB_HASH
or die "Cannot open $filename: $!\n" ;
$db->filter_fetch_key ( sub { $_ = unpack("i", $_) } ) ;
$db->filter_store_key ( sub { $_ = pack ("i", $_) } ) ;
$hash{123} = "def" ;
# ...
undef $db ;
untie %hash ; В этот раз были использованы только два фильтра — нам нужно было изменить только содержимое ключа, поэтому не было необходимости устанавливать какие-либо фильтры для значений.
Рекомендации и советы
Блокировка: проблемы с fd
До версии 1.72 этого модуля рекомендуемой техникой блокировки баз данных DB_File было использование flock для дескриптора файла, возвращаемого функцией "fd". К сожалению, эта техника была признана фундаментально ненадёжной (спасибо Дэвиду Харрису за обнаружение этой проблемы). Используйте её на свой страх и риск!
Техника блокировки выглядела так.
$db = tie(%db, 'DB_File', 'foo.db', O_CREAT|O_RDWR, 0644)
|| die "dbcreat foo.db $!";
$fd = $db->fd;
open(DB_FH, "+<&=$fd") || die "dup $!";
flock (DB_FH, LOCK_EX) || die "flock: $!";
...
$db{"Tom"} = "Jerry" ;
...
flock(DB_FH, LOCK_UN);
undef $db;
untie %db;
close(DB_FH); Проще говоря, вот что происходит:
-
Используйте "связь" для открытия базы данных.
-
Заблокируйте базу данных с помощью fd и flock.
-
Читаем и записываем в базу данных.
-
Разблокируйте и закройте базу данных.
Вот суть проблемы. Побочным эффектом открытия базы данных DB_File на шаге 2 является чтение начального блока из базы данных с последующим кэшированием в памяти.
Чтобы понять, почему это проблема, представьте, что два процесса, скажем, "A" и "B", оба хотят обновить одну и ту же базу данных DB_File, используя описанные выше шаги блокировки. Предположим, что процесс "A" уже открыл базу данных и имеет запись блокировки, но ещё не обновил базу данных (он завершил шаг 2, но ещё не начал шаг 3). Теперь процесс "B" пытается открыть ту же базу данных — шаг 1 будет выполнен успешно, но он заблокируется на шаге 2, до тех пор, пока процесс "A" не разблокирует её. Важно отметить, что на данный момент оба процесса будут иметь кэшированные идентичные начальные блоки из базы данных.
Теперь процесс "A" обновляет базу данных и случайно изменяет некоторые данные, хранящиеся в начальном буфере. Процесс "A" завершается, очищая все кэшированные данные на диск и освобождая блокировку базы данных. В этот момент данные базы данных на диске будут правильно отражать изменения, внесённые процессом "A".
После разблокировки процесс "B" может продолжить работу. Он также обновляет базу данных, и, к сожалению, также изменяет данные, которые были в его начальном буфере. Как только эти данные будут записаны на диск, они перезапишут часть/все изменения, внесённые процессом "A" в базу данных.
Результат этого сценария в лучшем случае — база данных, не содержащая ожидаемых данных. В худшем случае база данных будет повреждена.
Вышеописанное не произойдёт каждый раз, когда конкурирующие процессы обновляют одну и ту же базу данных DB_File, но это демонстрирует, почему этот метод не следует использовать.
Безопасные способы блокировки базы данных
Начиная с версии 2.x, Berkeley DB имеет встроенную поддержку блокировок. Вспомогательный модуль BerkeleyDB предоставляет интерфейс к этой функциональности блокировки. Если вы серьёзно относитесь к блокировке баз данных Berkeley DB, настоятельно рекомендуем использовать BerkeleyDB.
Если использование BerkeleyDB не является вариантом, на CPAN доступно несколько модулей, которые можно использовать для реализации блокировки. Каждый из них реализует блокировку по-своему и имеет разные цели. Поэтому важно знать различия, чтобы выбрать подходящий для вашего приложения. Вот три обёртки блокировки:
- Tie::DB_Lock
-
Обёртка DB_File, которая создаёт копии файла базы данных для чтения, таким образом, вы получаете своего рода многоверсионную конкурирующую систему чтения. Однако обновления всё ещё последовательны. Используйте для баз данных, где чтение может быть длительным и могут возникнуть проблемы с согласованностью.
- Tie::DB_LockFile
-
Обёртка DB_File, которая имеет возможность блокировать и разблокировать базу данных во время её использования. Избегает проблемы tie-before-flock, просто повторно связывая базу данных при получении или снятии блокировки. Благодаря гибкости в снятии и повторном получении блокировки в середине сеанса, это можно приспособить к системе, которая будет работать с длительными обновлениями и/или чтением, если приложение следует указаниям в документации POD.
- DB_File::Lock
-
Крайне лёгкая обёртка DB_File, которая просто блокирует файл блокировки перед связыванием базы данных и снимает блокировку после отвязки. Позволяет использовать один и тот же файл блокировки для нескольких баз данных, чтобы избежать тупиковых ситуаций, если это необходимо. Используйте для баз данных, где обновления и чтение быстрые и простые семантики блокировки flock достаточно.
Совместное использование баз данных с приложениями на C
Нет технических причин, по которым база данных Berkeley DB не может использоваться как Perl, так и приложением на C.
Большинство проблем, которые сообщаются в этой области, сводятся к тому, что C-строки завершаются нулём, в то время как Perl-строки — нет. См. "DBM-фильтры" для универсального способа решения этой проблемы.
Вот реальный пример. Netscape 2.0 сохраняет запись о посещаемых вами расположениях и времени последнего посещения в базе данных DB_HASH. Обычно это хранится в файле ~/.netscape/history.db. Поле ключа в базе данных — строка расположения, а поле значения — время последнего посещения расположения, хранящееся в виде 4-байтового двоичного значения.
Если вы ещё не догадались, строка расположения хранится с завершающим нулём. Это означает, что необходимо быть внимательным при доступе к базе данных.
Вот фрагмент кода, слабо основанный на скрипте ggh Тома Кристиансена (доступен из ближайшего архива CPAN в authors/id/TOMC/scripts/nshist.gz).
use warnings ;
use strict ;
use DB_File ;
use Fcntl ;
my ($dotdir, $HISTORY, %hist_db, $href, $binary_time, $date) ;
$dotdir = $ENV{HOME} || $ENV{LOGNAME};
$HISTORY = "$dotdir/.netscape/history.db";
tie %hist_db, 'DB_File', $HISTORY
or die "Cannot open $HISTORY: $!\n" ;;
# Dump the complete database
while ( ($href, $binary_time) = each %hist_db ) {
# remove the terminating NULL
$href =~ s/\x00$// ;
# convert the binary time into a user friendly string
$date = localtime unpack("V", $binary_time);
print "$date $href\n" ;
}
# check for the existence of a specific key
# remember to add the NULL
if ( $binary_time = $hist_db{"http://mox.perl.com/\x00"} ) {
$date = localtime unpack("V", $binary_time) ;
print "Last visited mox.perl.com on $date\n" ;
}
else {
print "Never visited mox.perl.com\n"
}
untie %hist_db ; Особенности untie()
Если вы используете API Berkeley DB, настоятельно рекомендуется прочитать "The untie Gotcha" в perltie.
Даже если вы в настоящее время не используете интерфейс API, стоит его прочитать.
Вот пример, который иллюстрирует проблему с точки зрения DB_File:
use DB_File ;
use Fcntl ;
my %x ;
my $X ;
$X = tie %x, 'DB_File', 'tst.fil' , O_RDWR|O_TRUNC
or die "Cannot tie first time: $!" ;
$x{123} = 456 ;
untie %x ;
tie %x, 'DB_File', 'tst.fil' , O_RDWR|O_CREAT
or die "Cannot tie second time: $!" ;
untie %x ; При выполнении скрипта будет выведено это сообщение об ошибке:
Cannot tie second time: Invalid argument at bad.file line 14. Хотя сообщение об ошибке выше относится к второму оператору tie() в скрипте, источник проблемы заключается в операторе untie(), который ему предшествует.
Прочитав perltie, вы, вероятно, уже догадались, что ошибка вызвана дополнительной копией связанного объекта, хранящейся в $X. Если нет, то проблема сводится к тому, что деструктор DB_File, DESTROY, не будет вызван, пока все ссылки на связанный объект не будут уничтожены. Обе переменные с связанным объектом, %x, и $X выше, содержат ссылку на объект. Вызов untie() уничтожит первую, но $X по-прежнему содержит действительную ссылку, поэтому деструктор не будет вызван, и файл базы данных tst.fil останется открытым. Тот факт, что Berkeley DB затем сообщает об попытке открыть базу данных, которая уже открыта через «Недопустимый аргумент», не помогает.
Если вы запустите скрипт с флагом -w, сообщение об ошибке станет:
untie attempted while 1 inner references still exist at bad.file line 12.
Cannot tie second time: Invalid argument at bad.file line 14. что точно определяет реальную проблему. Наконец, скрипт можно изменить, чтобы исправить исходную проблему, уничтожив объект API перед untie:
...
$x{123} = 456 ;
undef $X ;
untie %x ;
$X = tie %x, 'DB_File', 'tst.fil' , O_RDWR|O_CREAT
... ЧАСТО ЗАДАВАЕМЫЕ ВОПРОСЫ
Почему в моей базе данных есть Perl-код?
Если вы посмотрите на содержимое файла базы данных, созданного DB_File, там иногда может быть часть Perl-скрипта.
Это происходит потому, что Berkeley DB использует динамическую память для выделения буферов, которые впоследствии будут записаны в файл базы данных. Будучи динамической, память могла быть использована для чего угодно до того, как DB её выделил. Поскольку Berkeley DB не очищает память после её выделения, неиспользуемые части будут содержать случайный мусор. В случае, когда Perl-скрипт записывается в базу данных, случайный мусор будет соответствовать области динамической памяти, которая использовалась во время компиляции скрипта.
Если вы не против возможности встраивания частей ваших Perl-скриптов в файл базы данных, то об этом не стоит беспокоиться.
Как хранить сложные структуры данных с DB_File?
Хотя DB_File не может сделать этого напрямую, есть модуль, который может прозрачно работать поверх DB_File, чтобы достичь этого.
Изучите модуль MLDBM, доступный на CPAN в каталоге modules/by-module/MLDBM.
Что означает «широкий символ в записи подпрограммы»?
Вы обычно получаете это сообщение, если работаете с данными UTF-8 и хотите читать/записывать их из/в файл базы данных Berkeley DB.
Самый простой способ решения этой проблемы — использование предварительно определённого «utf8» DBM_Filter (см. DBM_Filter), который был разработан для решения этой ситуации.
Пример ниже показывает, что вам нужно, если оба ключ и значение должны быть в UTF-8.
use DB_File;
use DBM_Filter;
my $db = tie %h, 'DB_File', '/tmp/try.db', O_CREAT|O_RDWR, 0666, $DB_BTREE;
$db->Filter_Key_Push('utf8');
$db->Filter_Value_Push('utf8');
my $key = "\N{LATIN SMALL LETTER A WITH ACUTE}";
my $value = "\N{LATIN SMALL LETTER E WITH ACUTE}";
$h{ $key } = $value; Что означает «Недопустимый аргумент»?
Вы получите это сообщение об ошибке, когда один из параметров в вызове tie неверен. К сожалению, есть довольно много параметров, которые можно ввести неправильно, поэтому трудно понять, какой именно.
Вот несколько возможных вариантов:
-
Попытка повторного открытия базы данных без её закрытия.
-
Использование флага O_WRONLY.
Что означает «Не разрешено ключевое слово 'DB_File' без префиксов»?
Вы столкнётесь с этой конкретной ошибкой, когда в вашем скрипте используется pragma strict 'subs' (или полный pragma strict). Рассмотрим этот скрипт:
use warnings ;
use strict ;
use DB_File ;
my %x ;
tie %x, DB_File, "filename" ; Запуск этого скрипта приводит к ошибке:
Bareword "DB_File" not allowed while "strict subs" in use Чтобы обойти ошибку, поместите слово DB_File в одинарные или двойные кавычки, как показано ниже:
tie %x, "DB_File", "filename" ; Хотя это может показаться проблемой, это действительно стоит того, чтобы добавить pragma use strict во все ваши скрипты.
ССЫЛКИ
Статьи, которые либо о DB_File, либо используют его.
-
Полнотекстовый поиск в Perl, Тим Киентцле (tkientzle@ddj.com), Журнал доктора Добба, выпуск 295, январь 1999 г., стр. 34-41
ИЗМЕНЕНИЯ
Перемещено в файл изменений.
ОШИБКИ
В некоторых более старых версиях Berkeley DB были проблемы с записями фиксированной длины, использующими формат файла RECNO. Эта проблема была исправлена с версии 1.85 Berkeley DB.
Я уверен, что в коде есть ошибки. Если вы найдёте какие-либо ошибки или можете предложить какие-либо улучшения, ваши комментарии будут оценены.
ДОСТУПНОСТЬ
DB_File поставляется со стандартным дистрибутивом Perl. Ищите в каталоге ext/DB_File. Учитывая интервал между выпусками Perl, версия, поставляемая с Perl, скорее всего, устарела, поэтому самую последнюю версию всегда можно найти на CPAN (см. "CPAN" в perlmodlib для получения подробностей), в каталоге modules/by-module/DB_File.
Эта версия DB_File будет работать с версиями 1.x, 2.x или 3.x Berkeley DB, но ограничена функциональностью, предоставляемой версией 1.
Официальный веб-сайт Berkeley DB — http://www.oracle.com/technology/products/berkeley-db/db/index.html. Там доступны все версии Berkeley DB.
В качестве альтернативы, Berkeley DB версии 1 доступна в ближайшем архиве CPAN в src/misc/db.1.85.tar.gz.
АВТОРСКИЕ ПРАВА
Авторские права (c) 1995-2016 Пол Маркесс. Все права защищены. Эта программа является свободной программой; вы можете перераспределять её и/или изменять её в соответствии с теми же условиями, что и Perl сам.
Хотя DB_File защищён лицензией Perl, библиотека, которую он использует, а именно Berkeley DB, — нет. Berkeley DB имеет свои авторские права и свою лицензию. Пожалуйста, уделите время, чтобы её прочитать.
Вот несколько слов из FAQ Berkeley DB (по адресу http://www.oracle.com/technology/products/berkeley-db/db/index.html) относительно лицензии:
Do I have to license DB to use it in Perl scripts?
No. The Berkeley DB license requires that software that uses
Berkeley DB be freely redistributable. In the case of Perl, that
software is Perl, and not your scripts. Any Perl scripts that you
write are your property, including scripts that make use of
Berkeley DB. Neither the Perl license nor the Berkeley DB license
place any restriction on what you may do with them. Если у вас есть какие-либо сомнения относительно лицензии, обратитесь к авторам Berkeley DB или автору DB_File. См. "АВТОР" для получения подробностей.
СМОТРИТЕ ТАКЖЕ
perl, dbopen(3), hash(3), recno(3), btree(3), perldbmfilter, DBM_Filter
АВТОР
Интерфейс DB_File был написан Полом Маркессом <pmqs@cpan.org>.
© 1993–2020 Larry Wall and others
Licensed under the GNU General Public License version 1 or later, or the Artistic License.
The Perl logo is a trademark of the Perl Foundation.
https://perldoc.perl.org/5.28.3/DB_File