В качестве примера использования Turbo Access будет заново с
применением его возможностей написана программа ведения простого
почтового списка, разработанная в главе 2, которая использовала
связанные ссылки. Во-первых, нам необходимо найти размер записи,
используемой для хранения адресной информации, а затем вы приме-
ните программу SETCONST для вычисления соответствующих значений
шести констант, требующихся для процедур Turbo Access.
Так как Turbo Access использует первые два байта в каждой
записи в качестве флага индикации удаления, необходимо добавить
поле целой переменной к исходной адресной записи. Кроме того, по-
ля "предыдущий" и "следующий" больше не используются. Результиру-
ющая запись выглядит следующим образом:
type
address = record
status: integer; { используется Turbo Access }
name: string[30];
street: string[40];
city: string[20];
state: string[2];
zip: string[9];
end;
Длина в байтах поля adress, которая найдена, используя SizeOf,
равна 108. Вам нужно будет данное число при выполнении программы
SETCONST.PAS.
Программа SETCONST.PAS определяет значения констант, необхо-
димых для Turbo Access. При запуске программы появляется экран,
аналогичный показанному на рис.9-2, со значениями, принимаемыми
по умолчанию. Руководство пользователя по инструментарию баз дан-
ных говорит, что для большинства применений вам необходимо изме-
нить только длину записи и длину ключа, чтобы подстроиться под
ваши особенности. Необходимо также изменить число записей, запо-
минаемых в базе данных, если оно превышает 10000. Размер страницы
и размер стека страниц изменять не надо. Длина записи adress рав-
на 108, так что сначала надо ввести это значение. В данном приме-
ре поле name будет использоваться в качестве ключа, поэтому зна-
чение 30 вводится как длина ключа. Клавишу RETURN нажать для
оставшихся полей. Когда информация введена, SETCONT.PAS отобража-
ет экран, показанный на рис.9 -3.
При выходе из SETCONST.PAS вы можете автоматически создать
соответствующую декларацию const. Декларация для программы
** 1 - рабочий лист задания констант Turbo Access, версия
1.10А **
размер записи данных (байт) 200 200
длина строки ключа (символов) 10
размер базы данных (записей) 10000
размер страницы (ключей) 24
размер стека страниц (страниц) 10
плотность (процент используемых элементов в средней странице)
50% 75% 100%
общее количество страниц индексного файла
память, используемая для стека страниц (байт)
размер страницы индексного файла (байт)
размер индексного файла (байт)
размер файла данных (байт)
порядок
максимальная высота
среднее количество просмотров, необходимое для нахождения ключа
среднее количество просмотров, удовлетворяемое стеком страниц
среднее количество просмотров на диске для нахождения ключа
нажмите ESC для завершения программы
Рис.9-2. Начальный экран SETCONST.PAS
** Turbo Access constant determination worksheet, Version 1.10A
Data record size (bytes) 108
Key string Iength (characters) 30
Size of the database (records) 10000
Page size (keys) 24
Page stack size (pages) 10
Density (Percent of Items in use per average Page)50% 75% 100%
Total Index file pages 834 556 417
Memory used for page stack (bytes) 8430 8430 8430
Index file page size (bytes) 843 843 843
Index file size (bytes)
Data file size (bytes)
Order 12 12 12
MaxHeight 4 4 3
Average searches needed to find a key 3.71 3.19 2.90
Average searches satisfied by page stack 1.75 1.50 1.38
Average disk searches needed to find a key 1.96 1.69 1.52
ESC to end program
Рис.9-3. Экран SETCONST.PAS с вычисленными значениями:
почтового списка показаны ниже. Комментарии даны автором.
Const
{данные константы сгенерированы программой SETCONST.PAS,
предоставляемой инструментарием баз данных. }
MaxDataRecSize = 108;
MaxKeyLen = 30;
PageSize = 24;
Order = 12;
PageStackSize = 10;
MaxHeight = 4;
данные константы сгенерированы программой SETCONST.PAS, предос-
тавляемой инструментарием баз данных.
С данной информацией и включенными необходимыми файлами
Turbo Access первая часть программы почтового списка выглядит
следующим образом:
program db_example;
Const
{данные константы сгенерированы программой SETCONST.PAS,
предоставляемой инструментарием баз данных. }
MaxDataRecSize = 108;
MaxKeyLen = 30;
PageSize = 24;
Order = 12;
PageStackSize = 10;
MaxHeight = 4;
type
address = record
status: integer; {используется Turbo Access }
name: string[30];
street: string[40];
city: string[20];
state: string[2];
zip: string[9];
end;
{следующие файлы содержат процедуры баз данных}
{$i access.box} {основные процедуры баз данных}
{$i addkey.box} {добавить элементы }
{$i delkey.box} {удалить элементы }
{$i getkey.box} {поиск по дереву }
var
dbfile: DataFile;
ifile: IndexFile;
done: boolean;
В основном тексте программы, показанном далее, сначала ини-
циализируется таблица индексов с помощью процедуры InitIndex. Да-
лее либо открываются, либо создаются соответствующие файлы данных
и индексный. Основной цикл программы аналогичен тому, который
разработан в главе 2, и позволяет пользователю выбрать различные
опции. При завершении файлы данных и индексный закрываются.
begin
InitIndex;
OpenFile(dbfile, 'mail.lst', SizeOf(address));
if not OK then
begin
WriteLn('creating new data file');
MakeFile(dbfile, 'mail.lst', SizeOf(address));
end;
OpenIndex(ifile, 'mail.ndx', 30, 0);
if not OK then
begin
WriteLn('creating new index file');
MakeIndex(ifile, 'mail.ndx', 30, 0);
end;
done:=false;
repeat
case MenuSelect of
'1': Enter;
'2': Remove;
'3': ListAll;
'4': Search;
'5': Update;
'6': done:=true;
end;
until done;
CloseFile(dbfile);
CloseIndex(ifile);
end.
Отметим, что больше не необходимо явно загружать и сохранять
почтовый список: процедуры Turbo Access объявляют файлы автомати-
чески.
Процедура Enter, показанная далее, вводит адресную информа-
цию, запоминает ее в файле данных и помещает ключ в индексный
файл. Отметим, что поле status каждой записи установлено в 0. По
соглашению Turbo Access использует 0 для обозначения активной за-
писи, а не нулевое значение для обозначения уничтоженных элемен-
тов.
temp: string[30];
info: address;
begin
done:=FALSE;
repeat
Write('Enter name:');
Read(info.name); WriteLn;
if Length(info.name)=0 then done:=TRUE
else
begin
Write('Enter street: ');
Read(info.street); WriteLn;
Write('Enter city: ');
Read(info.city); WriteLn;
Write('Enter state: ');
Read(info.state); WriteLn;
Write('Enter zip: ');
Read(info.zip); WriteLn;
info.status:=0; {сделать активной }
FindKey(ifile, recnum, info.name);
if not OK then {убедитесь, что нет дублированных
ключей }
begin
AddRec(dbfile, recnum, info);
AddKey(ifile, recnum, info.name);
end else WriteLn('Duplicate key ignored');
end;
until done;
end; {Enter}
Как вы видите, данная процедура осуществляет проверку на
дублирование ключей. Так как дублированные имена не допускаются,
процедура, во-первых, проверяет, соответствует ли новый ключ ка-
кому-либо уже существующему в файле. Если это так, то он игнори-
руется. Порядок вызова AddRec и AddKey критичен, так как перемен-
ная RecNum должна быть сначала установлена процедурой AddRec и
затем запомнена процедурой AddKey (Помните, что номер записи фай-
ла данных связан с ключем в индексном файле и используется для
последующего нахождения данных).
Процедура ListAll рассчитывает все содержимое почтового
списка:
procedure ListAll;
{добавить адрес к списку}
procedure Enter;
var
done: boolean;
recnum: integer;
var
info: address;
len, recnum: integer;
begin
len: = filelen(dofile) -1;
for recnum:=1 to len do
begin
GetRec(dbfile, recnum, info);
{display if not deleter}
if info.status = 0 then display(info);
end;
end; {ListAll}
Процедура FileLen возвращает число записей активных или
уничтоженных в файле данных, включая первую запись, которая заре-
зервирована для использования Turbo Access. Следовательно, дейс-
твительное число пользовательских записей на единицу меньше. Кро-
ме того, из-за того, что некоторые записи могут быть уничтожены,
необходимо проверять поле status до отображения информации.
Поиск определенного адреса включает в себя поиск ключа в ин-
дексном файле с помощью процедуры FindKey. Когда ключ найден,
возвращается соответствующий номер записи в файле данных и он ис-
пользуется процедурой GetRec для получения нужной информации.
Процедура Search, показанная далее реализует данный подход:
{найти заданный элемент}
procedure Search;
var
name: string[30];
recnum: integer;
info: address;
begin
Write('Enter name: ');
ReadLn(name);
{найти ключ,если он существует}
FindKey(ifile, recnum, name);
if OK then { если найден }
begin
GetRec(dbfile, recnum, info);
{display if not deleter}
if info.status = 0 then Display(info);
end else WriteLn('not found');
end; {Search}
Наконец, модификация существующей записи предполагает, что
вы должны сначала найти запись, считать ее, модифицировать и за-
писать обратно в файл данных. Процедура Update иллюстрирует прос-
той метод обновления, в котором пользователь должен ввести заново
всю информацию. Более сложные подходы требуют перевода только из-
меняемых полей.
{ изменение адреса в списке, исключая поле имени }
procedure Update;
var
done: boolean;
recnum: integer;
temp: string[30];
info: address;
begin
Write('Введите имя: ');
Read(info.name); WriteLn;
FindKey(ifile, recnum, info.name);
if OK then
begin
Write('Введите улицу: )';
Read(info.street); WriteLn;
Write('Введите город: ');
Read(info.city); WriteLn;
Write('Введите штат: ');
Read(info.state); WriteLn;
Write('Введите индекс: ');
Read(info.zip); WriteLn;
info.status:=0; {сделать активной}
PutRec(dbfile, recnum, info);
end else WriteLn('ключ не найден');
end; {Update}
Целиком программа, использующая Turbo Access для работы с
файлами, выглядит следующим образом:
program db_example;
Const
{данные константы сгенерированы программой SETCONST.PAS,
предоставляемой инструментарием баз данных. }
MaxDataRecSize = 108;
MaxKeyLen = 30;
PageSize = 24;
Order = 12;
PageStackSize = 10;
MaxHeight = 4;
type
address = record
status: integer; {используется Turbo Access }
name: string[30];
street: string[40];
city: string[20];
state: string[2];
zip: string[9];
end;
{следующие файлы содержат процедуры баз данных}
{$i access.box} {основные процедуры баз данных}
{$i addkey.box} {добавить элементы }
{$i delkey.box} {удалить элементы }
{$i getkey.box} {поиск по дереву }
var
dbfile: DataFile;
ifile: IndexFile;
done: boolean;
function MenuSelect:char; {возврат пользовательского
выбора }
var
ch:char;
begin
WriteLn('1. ');
WriteLn('2. Удалить имя ');
WriteLn('3. Отобразить список');
WriteLn('4. Обновление ');
WriteLn('5. Поиск по имени ');
WriteLn('6. Выход ');
repeat
WriteLn;
Write('Введите ваш выбор:');
Read(ch); ch:=UpCase(ch); WriteLn;
until (ch>='1') and (ch<='6');
MenuSelect:=ch;
end; {MenuSelect}
{добавить адрес к списку}
procedure Enter;
var
done: boolean;
recnum: integer;
temp: string[30];
info: address;
begin
done:=FALSE;
repeat
Write('Введите имя: ');
Read(info.name); WriteLn;
if Length(info.name)=0 then done:=TRUE
else
begin
Write('Введите улицу: ');
Read(info.street); WriteLn;
Write('Введите город: ');
Read(info.city); WriteLn;
Write('Введите штат: ');
Read(info.state); WriteLn;
Write('Введите индекс: ');
Read(info.zip); WriteLn;
info.status:=0; {сделать активной}
FindKey(ifile, recnum, info.name);
if not OK then {убедитесь, что нет дублированных
ключей }
begin
AddRec(dbfile, recnum, info);
AddKey(ifile, recnum, info.name);
end else WriteLn('Дублированный ключ игнорирован');
end;
until done;
end; {Enter}
{ изменение адреса в списке, исключая поле имени }
procedure Update;
var
done: boolean;
recnum: integer;
temp: string[30];
info: address;
begin
Write('Введите имя: ');
Read(info.name); WriteLn;
FindKey(ifile, recnum, info.name);
if OK then
begin
Write('Введите улицу: ');
Read(info.street); WriteLn;
Write('Введите город: ');
Read(info.city); WriteLn;
Write('Введите штат: ');
Read(info.state); WriteLn;
Write('Введите индекс: ');
Read(info.zip); WriteLn;
info.status:=0; {сделать активной}
PutRec(dbfile, recnum, info);
end else WriteLn('ключ не найден');
end; {Update}
{удалить адрес из списка }
procedure Remove;
var
recnum: integer;
name: string[30];
info: address;
begin
Write('Введите имя для удаления : ');
Read(name); WriteLn;
FindKey(ifile, recnum, name);
if OK then
begin
DeleteRec(dbfile, recnum);
DeleteKey(ifile, recnum, name);
end else WriteLn('Не найдено');
end; {Remove}
procedure Display(info: address);
begin
WriteLn(info.name);
WriteLn(info.street);
WriteLn(info.city);
WriteLn(info.state);
WriteLn(info.zip); WriteLn;
end; {Display}
procedure ListAll;
var
info: address;
len, recnum: integer;
begin
len := fileLen(dbfile) -1;
for recnum:=1 to len do
begin
GetRec(dbfile, recnum, info);
{отобразить, если не уничтожен}
if info.status = 0 then display(info);
end;
end; {ListAll}
{Найти заданный элемент }
procedure Search;
var
name: string[30];
recnum: integer;
info: address;
begin
Write('Введите имя: ');
ReadLn(name);
{найти ключ, если существует}
FindKey(ifile, recnum, name);
if OK then
begin
GetRec(dbfile, recnum, info);
{отобразить, если не уничтожен}
if info.status = 0 then Display(info);
end else WriteLn('не найден');
end; {Search}
begin
InitIndex;
OpenFile(dbfile, 'mail.lst', SizeOf(address));
if not OK then
begin
WriteLn('Cоздание нового файла');
MakeFile(dbfile, 'mail.lst', SizeOf(address));
end;
OpenIndex(ifile, 'mail.ndx', 30, 0);
if not OK then
begin
WriteLn('Cоздание нового файла ');
MakeIndex(ifile, 'mail.ndx', 30, 0);
end;
done:=false;
repeat
case MenuSelect of
'1': Enter;
'2': Remove;
'3': ListAll;
'4': Search;
'5': Update;
'6': done:=true;
end;
until done;
CloseFile(dbfile);
CloseIndex(ifile);
end.