Страница 9 из 39
Пример простого почтового списка В качестве примера использования 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.
|