Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Лучшие статьи журнала ╚Компьютеры+Программы╩


Информационный Канал Subscribe.Ru

Здравствуйте, уважаемые читатели!

В этом выпуске рассылки публикуется статья, занявшая по результатам голосования третье место.


Сергей Кривошеев,
sergk@mail.spbnit.ru

База данных без BDE:
прямое обращение к содержимому файла

Отношение прикладных программистов к системе разработки приложений Borland Delphi неоднозначно. Но все сходятся в одном: это система — профессиональный механизм, намного ускоряющий создание различных программ. Входящие в состав VCL компоненты доступа и отображения данных значительно упрощают разработку приложений для работы с БД, а возможности по работе с различными серверными и настольными СУБД, предоставляемые Borland Database Engine, многим кажутся уже чем-то само собой разумеющимся.

К сожалению, за все надо платить. Цена преимуществ BDE — невозможность распространять разработки без установки на ПК клиента этой самой Engine. Последнее приводит к увеличению размера дистрибутива в несколько раз и соответствующим неудобствам при распространении ПО. А если на компьютере пользователя уже установлено программное обеспечение, использующее более новую версию BDE, вам придется еще и устранять конфликты версий. Неудивительно, что программист дважды подумает, прежде чем создавать на таких условиях простое приложение для ввода, просмотра и модификации некоего набора данных.

Можно ли мы обойтись без BDE, не нанося ущерба проекту? В принципе, нет ничего невозможного. Например, для хранения информации можно использовать типизированные файлы, состоящие из записей (record). Давайте присмотримся внимательнее к этому пути развития non-BDE-движения.

В первую очередь хочу предостеречь от эйфории. Не думайте, что после прочтения этой статьи вы запросто сможете создавать приложения для работы с базами данных без BDE. В частности, при использовании типизированных файлов записей программист теряет возможность применять в своих проектах мощнейшие механизмы, предоставленные компонентами доступа к данным Data Access и Data Controls из состава VCL. Нам придется заново разрабатывать все процедуры, которые понадобятся для просмотра и модификации информации.

Звучит не очень-то обнадеживающе? Подозреваю, что именно эти аргументы отпугивают большинство разработчиков от создания собственных, независимых от BDE механизмов для работы с данными. И это весьма печально: пускай задача создания базы данных не тривиальна, все же в ней нет ничего такого, что не было бы по силам даже начинающему программисту. Кроме того, при работе с файлом записей вы ограничены лишь теми действиями, которые можно выполнить над данной информационной структурой — не больше.

Мы не станем создавать некий универсальный движок, на основе которого в дальнейшем будут функционировать приложения масштаба предприятия. В нашу задачу входит лишь реализация нескольких простых методов, которые, однако, позволят освободиться от монстра по имени Borland Database Engine.

Многие программисты, особенно начинающие, совершают одну и ту же ошибку: недостаточно прорабатывают функциональность создаваемого приложения. Зачастую разработчик определяет только ряд свойств, которыми должна обладать программа, и наращивает остальные функции вокруг наскоро созданного «костяка». Вместо того чтобы совершать второпях необдуманные действия, возьмем в руки карандаш и представим на бумаге, что должна делать наша программа. Такое описание функциональности проекта может сводиться к простому списку на одном листке, а может представлять собой несколько толстых томов с таблицами, диаграммами, графиками и т.д. Независимо от объема, эти подготовительные материалы намного облегчат дальнейшую работу над проектом.

Чего мы хотим?

Функциональность любого элементарного приложения для работы с базами данных «вертится» вокруг следующих трех действий:

  • добавление новой записи;
  • удаление записи;
  • модификация существующей записи.

Кроме того, пользователю необходимы средства навигации по набору данных. В «джентльменский набор» такого рода функций входят следующие:

  • перемещение к первой записи в файле (аналог TDataSet.First);
  • перемещение к последней записи в файле (аналог TDataSet.Last);
  • переход к следующей записи (аналог TDataSet.Next);
  • переход к предыдущей записи (аналог TDataSet.Prior);
  • поиск по таблице (аналог TDataSet.Locate).

Вот, собственно, и все, что требуется для работы с базами данных. Обратите внимание, что среди перечисленных функций отсутствует печать. Это сделано сознательно: создание твердой копии является скорее сервисной функцией, нежели базовой. А мы пока говорим только о минимальном наборе необходимых методов.

Предположим, что хранилищем данных служит двоичный файл, записи в котором размещаются последовательно и имеют следующую структуру:

type
MyRecord = record
ID:Integer;
FirstName,
LastName:string;
end;

Если до сих пор вы работали с массивами записей, то, думаю, вам будет удобнее рассматривать файл записей как некий массив однотипных элементов (записей). Механизм работы с файлами записей в Delphi аналогичен работе с любым другим файлом: файлу ставится в соответствие некоторая файловая переменная, затем файл открывается соответствующей функцией, в нем выполняются необходимые манипуляции, после чего файл закрывается. Однако для успешной работы с файлом записей следует учесть следующие особенности этой информационной структуры.

  • Файл записей подобен массиву, однако в отличие от массива Object Pascal, он может увеличиваться в размере (а также уменьшаться, но этот аспект мы рассмотрим ниже).
  • Файл записей — это двоичный файл, поэтому вы не сможете редактировать его содержимое, используя простой текстовый редактор; все манипуляции с данными, хранящимися в таком файле, выполняются только с помощью специально разработанного приложения.
  • Подобно тому, как для работы с массивом указывается переменная соответствующего типа, необходимо описать файловую переменную как file of <type>.
  • Функции Read и Write изменяют указатель файла так же, как функции ReadLn и WriteLn переводят указатель текстового файла на следующую строку (обратите внимание, что функции Read и Write для текстовых файлов не изменяют значение указателя).
  • Как и элементу массива, каждой записи в файле неявно назначается номер, начиная с 0. Для перехода к произвольной позиции в пределах файла используется функция Seek.

Открытие и закрытие файла записей

Как и при работе с любым типом файлов (например, текстовыми файлами), для доступа к файлу записей используется процедуры AssignFile, Reset (для открытия существующего файла) и Rewrite (для создания нового). Для того чтобы закрыть файл, следует воспользоваться процедурой CloseFile. Те, кто еще помнят времена программирования в Borland Pascal, скорее всего, обратили внимание, что в этом перечне отсутствуют старые добрые Assign и Close. Нет, они не потеряли своей функциональности. Однако имена этих процедур конфликтуют с методами некоторых классов, описанных в VCL. Таким образом, для того чтобы использовать процедуры Assign и Close, не опасаясь ошибки компиляции, необходимо вызывать их с явным указанием модуля, в котором они описаны (например: system.Assign).

Далее, вы не сможете открыть файл записей, пока не определена структура записи и переменная, которая будет ставиться в соответствие файлу. Например, если в файле хранится информация о людях (аналог электронной записной книжки), то структура записи может выглядеть примерно так:

type
TAddressRec = record
FirstName:string [20];
LastName:string [20];
Add1:string [40];
Add2:string [40];
City:string [30];
Zip:string [10];
Phone:string [20];
Fax:string [20];
end;

Переменная для работы с файлом должна быть описана так:

var
AddrFile:File of TAddressRec;

Теперь мы можем открыть файл:

AssignFile (AddrFile, 'Address.DAT');
if FileExists ('Address.DAT') then
Reset (AddrFile)
else
Rewrite (AddrFile);

Стандартная функция FileExists, как вы уже, вероятно, догадались, используется для определения существования файла.

Переход к определенной позиции в файле

Каждой записи в файле по умолчанию присваивается номер, начиная с нуля. Таким образом, индекс последнего элемента в файле равен количеству записей минус 1. таким образом, используя файловую переменную, программист может обращаться к записи в файле по номеру, используя функцию Seek:

Seek (AddrFile, 50);

При открытии файла указатель устанавливается на первую запись. Для того чтобы прочитать или записать информацию, нужно переместить файловый указатель на интересующую нас запись.

Обратите внимание, что каждое действие по записи или чтению данных перемещает файловый указатель на следующую позицию (запись). Например, если мы установили файловый указатель на запись номер 50, то после чтения информации, содержащейся в этой записи, файловый указатель станет равным 51.

Это следует учитывать при последовательном чтении всех записей файла, потому что по достижении конца файла при попытке прочитать следующую (несуществующую) запись будет выдано сообщение об ошибке. Во избежание таких ситуаций следует определять текущую позицию с помощью функции FilePos. Она возвращает номер записи, которая будет обрабатываться следующей операцией ввода/вывода. К примеру, если вы хотите обработать все записи в файле, то следует организовать цикл, в котором бы текущее значение FilePos сравнивалось бы с размером файла или использовалась функция EOF. Реализация первого варианта могла бы выглядеть примерно так:

While (FilePos (AddrFile) <= (FileSize (AddrFile) — 1)) do begin
Read (AddrFile, AddrRec);
//. работаем с AddrRec
end;

Чтение и запись информации

Теперь, когда мы определились с механизмами открытия, закрытия и навигации по файлу записей, давайте перейдем к обсуждению процессов чтения и редактирования хранящейся в нем информации. Для того, чтобы прочитать запись из файла, достаточно воспользоваться процедурой Read, передав ей в качестве параметров файловую переменную и переменную, в которую следует прочитать запись:

Read (AddrFile, AddrRec);

Для записи в файл аналогичным образом используется функция Write:

Write (AddrFile, AddrRec);

Одной командой можно обрабатывать сразу несколько записей:

Read (AddrFile, AddrRec1, AddrRec2, AddrRec3);

Например, для изменения фамилии в базе данных можно использовать следующий код:

procedure ChangeLastName (RecNum: Integer; NewValue: string);
var
recBuf: TAddressRec;
begin
Seek (AddrFile, RecNum);
Read (AddrFile, recBuf);
with recBuf do
LastName:= NewValue;
{Возвращаемся к прочитанной записи и переписываем ее}
Seek (AddrFile, RecNum);
Write (AddrFile, recBuf);
end;

Данную процедуру можно усовершенствовать, добавив в нее, например, параметр, указывающий, какое именно поле записи следует изменить. Код усложнится, зато процедура станет универсальнее:

procedure ChangeField (RecNum: Integer; FieldName: string; NewValue: string);
var
recBuf: TAddressRec;
begin
Seek (AddrFile, RecNum);
Read (AddrFile, recBuf);
with recBuf do
if FieldName = 'FirstName' then FirstName:= NewValue
else if FieldName = 'LastName' then LastName:= NewValue
else if FieldName = 'Add1' then Add1:= NewValue
else if FieldName = 'Add2' then Add2:= NewValue
else if FieldName = 'City' then City:= NewValue
else if FieldName = 'Zip' then Zip:= NewValue
else if FieldName = 'Phone' then Phone:= NewValue
else if FieldName = 'Fax' then Fax:= NewValue
Seek (AddrFile, RecNum);
Write (AddrFile, recBuf);
end;

И это лишь один из возможных вариантов…

Вставка и удаление записей

Работая с информацией, размещенной в файле записей, следует помнить, что в Delphi отсутствуют механизмы вставки и удаления записей в середине файла. Решение этой задачи требует самостоятельных действий, а именно использования временного массива для хранения информации. При вставке или удалении записи все содержимое файла загружается во временный буфер. Там с ним выполняются необходимые действия, после чего весь массив записывается обратно в файл.

Рисунок: Программа может выглядеть так

Создаем программу

Теперь мы знаем достаточно, чтобы самостоятельно разработать приложение с требуемой функциональностью. Пример, который мы рассмотрим, не претендует на функциональную полноту и универсальность. Здесь мы приведем исходный текст основного модуля проекта с необходимыми комментариями и внешний вид главной формы, чтобы вам было легче понять логику работы каждой функции.

Функции для работы с файлами
Функция/процедура Назначение
procedure AssignFile (var F;Filename:String); Ставит в соответствие файлу с именем FileName файловую переменную F.
procedure Reset (var F [;Size:word]); Открывает существующий файл: для текстовых файлов — только для чтения, для остальных — для чтения и записи. Параметр Size указывает размер блока данных и имеет смысл только для нетипизированных файлов.
procedure Rewrite (var F [;Size:word]); Создает и открывает новый файл: для текстовых файлов — только для записи, для остальных — для чтения и записи. Параметр Size указывает размер блока данных и имеет смысл только для нетипизированных файлов.
Procedure CloseFile (var F); Закрывает файл, сохраняя при этом связь файловой переменной F c именем файла, установленную ранее процедурой AssignFile.
function FileSize (var F): LongInt; Возвращает количество компонентов (записей) файла.
function FilePos (var F): LongInt; Возвращает текущую позицию в файле, т.е. номер компоненты (записи), которая будет обрабатываться следующей операцией ввода/вывода.
procedure Seek (var F; N:logint); Смещает указатель файла F на запись с номером N (нумерация начинается с 0)
procedure Read (var F; v1,..., vn); Читает данные из типизированного файла F. Переменные v1,..., vn должны быть того же типа, что и компоненты (записи) файла.
procedure Write (var F; P1,...,Pn); Записывает данные в типизированный файл F. Переменные P1,...,Pn должны быть того же типа, что и компоненты (записи) файла.
procedure Truncate (var F); Усекает файл F, начиная с текущей записи. Процедура применима ко всем типам файлов, за исключением текстовых.
function EOF (var F):boolean; Возвращает True в том случае, если указатель текущей позиции в файле находится за последней компонентой (записью) файла. В противном случае возвращает False.
procedure Erase (var F); Уничтожает файл. Файл предварительно надо закрыть.
function IOResult:integer; Возвращает 0, если последняя операция ввода/вывода завершилась успешно, в противном случае — 1.

Файлы проекта:

unit Main;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ComCtrls, ExtCtrls, ToolWin;

{Описываем структуру записи}
type
TAddressRec = record
FirstName: string [20];
LastName : string [20];
Add1   : string [40];
Add2   : string [40];
City   : string [30];
Zip   : string [10];
Phone  : string [20];
Fax   : string [20];
end;

{Тип перемещения по файлу}
type
TRecMove = (recFirst, recLast, recNext, recPrev);

type
TFormMain = class (TForm)
StatusBar: TStatusBar;
gbSearch: TGroupBox;
gbData: TGroupBox;
lblFirstName: TLabel;
lblLastName: TLabel;
LblAdd1: TLabel;
lblAdd2: TLabel;
lblCity: TLabel;
lblZip: TLabel;
lblPhone: TLabel;
lblFax: TLabel;
EditFirstName: TEdit;
EditLastName: TEdit;
EditAdd1: TEdit;
EditAdd2: TEdit;
EditCity: TEdit;
EditZip: TEdit;
btnNew: TButton;
btnDelete: TButton;
btnUpdate: TButton;
EditPhone: TEdit;
EditFax: TEdit;
btnInsert: TButton;
ToolBar: TToolBar;
btnFirst: TButton;
btnPrev: TButton;
btnNext: TButton;
btnLast: TButton;
EditSearchByLastName: TEdit;
btnFind: TButton;
btnFindNext: TButton;
procedure FormCreate (Sender: TObject);
procedure btnNewClick (Sender: TObject);
procedure btnFirstClick (Sender: TObject);
procedure btnLastClick (Sender: TObject);
procedure btnNextClick (Sender: TObject);
procedure btnPrevClick (Sender: TObject);
procedure FormClose (Sender: TObject; var Action: TCloseAction);
procedure btnDeleteClick (Sender: TObject);
procedure btnUpdateClick (Sender: TObject);
procedure btnFindClick (Sender: TObject);
procedure btnFindNextClick (Sender: TObject);
procedure btnInsertClick (Sender: TObject);
private
procedure SetRecVals;
procedure ReadRec;
procedure EnableButtons (EnableIt: Boolean);
procedure MoveToRec (Direction: TRecMove);
procedure LocateRec (Value: string; FromBOF: Boolean);
procedure CreateNewRec;
procedure InsertRec;
procedure UpdateRec;
procedure DeleteRec;
procedure EmptyEdits;
public
{Public declarations}
NewRec: Boolean;
end;

var
FormMain: TFormMain;
AddrFile : File of TAddressRec;
AddrRec : TAddressRec;

const
MAXRECS = 2000;

implementation

{$R *.DFM}
{=============Базовые методы формы================}
procedure TFormMain.FormCreate (Sender: TObject);
begin
AssignFile (AddrFile, 'Address.DAT');
if FileExists ('Address.DAT') then begin
Reset (AddrFile);
if (FileSize (AddrFile) > 0) then begin
Seek (AddrFile, 0);
ReadRec;
NewRec:= False;
end;
end
else begin
Rewrite (AddrFile);
NewRec:= True;
end;
btnNew.Enabled:= True;
btnInsert.Enabled:= False;
btnUpdate.Caption:= '&Записать';
end;

procedure TFormMain.FormClose (Sender: TObject; var Action: TCloseAction);
begin
CloseFile (AddrFile);
end;

{=============Процедуры общего назначения===============}

{Читаем данные из элементов ввода в поля записи}
procedure TFormMain.SetRecVals;
begin
with AddrRec do begin
FirstName:= EditFirstName.Text;
LastName := EditLastName.Text;
Add1   := EditAdd1.Text;
Add2   := EditAdd2.Text;
City   := EditCity.text;
Zip   := EditZip.Text;
Phone  := EditPhone.Text;
Fax   := EditFax.Text;
end;
end;

{Читаем текущую запись из файла и заполняем соответствующие элементы ввода}
procedure TFormMain.ReadRec;
begin
Read (AddrFile, AddrRec);
with AddrRec do begin
EditFirstName.Text:= FirstName;
EditLastName.Text:= LastName;
EditAdd1.Text:= Add1;
EditAdd2.Text:= Add2;
EditCity.text:= City;
EditZip.Text:= Zip ;
EditPhone.Text:= Phone;
EditFax.Text:= Fax;
end;

{Перемещаеся на начало только что прочитанной записи}
Seek (AddrFile, FilePos (AddrFile) — 1);
end;

procedure TFormMain.EnableButtons (EnableIt: Boolean);
begin
btnNew.Enabled:= EnableIt;
btnFirst.Enabled:= EnableIt;
btnPrev.Enabled:= EnableIt;
btnNext.Enabled:= EnableIt;
btnLast.Enabled:= EnableIt;
{Кнопки «Новая запись« и «Вставить« не должны быть
одновременно активны или неактивны}
btnInsert.Enabled:= NOT EnableIt;
end;

{=============Процедуры манипулирования данными ==============}

{Просто подготавливаем форму для ввода данных}
procedure TFormMain.CreateNewRec;
begin
EmptyEdits;
NewRec:= True;
EnableButtons (False);
btnUpdate.Caption:= '&Записать в конец';
end;

{Вставляем запись}
procedure TFormMain.InsertRec;
var curPos, numRecs, i: Integer;
{Для простоты здесь используется массив, однако можно применить для решения
той же задачи, к примеру, TList}
RecBuf: array [0..MAXRECS] of TAddressRec;
begin
{1.Загружаем информацию из элементов управления во временную
структуру в памяти}
SetRecVals;

{2.Определяем текущую позицию в файле}
curPos:= FilePos (AddrFile);

{3.Получаем количество записей в файле}
numRecs:= FileSize (AddrFile);

{4.Читаем из файла записи, которые должны располагаться перед вставляемой
записью. После этого цикла файловый указатель должен соответствовать
тому номеру, который должна иметь вставляемая запись}
if FilePos (AddrFile) > 0 then begin
i:= 0;
Seek (AddrFile, 0);
while FilePos (AddrFile) < curPos do begin
Read (AddrFile, RecBuf [i]);
Inc (i);
end;
end;

{5.Добавляем вставляемую запись в массив}
RecBuf [curPos]:= AddrRec;

{6.Далее добавляем в массив все те записи, которые еще не были прочитаны
из файла. В массиве они будут располагаться после вставленной записи}
i:= curPos + 1;
while NOT eof (AddrFile) do begin
Read (AddrFile, RecBuf [i]);
Inc (i);
end;

{7.Записивыем весь массив в файл. Это можно сделать, к примеру, вот так:}
i:= 0;
Seek (AddrFile, 0);
while (i <= numRecs) do begin
Write (AddrFile, RecBuf [i]);
Inc (i);
end;

{8.Устанавливаем указатель на только что вставленную запись}
Seek (AddrFile, curPos);
ReadRec;
btnUpdate.Caption:= '&Записать';
EnableButtons (True);
end;

{Модификация существующей записи или добавление новой записи в конец файла}
procedure TFormMain.UpdateRec;
var curPos: Integer;
begin
curPos:= FilePos (AddrFile);
{1.Загружаем информацию из элементов управления во временную
структуру в памяти}
SetRecVals;

{2.Если это новая запись, то переходим в конец файла — в изменении
текущей записи нет необходимости}
if NewRec then begin
Seek (AddrFile, FileSize (AddrFile));
curPos:= FileSize (AddrFile) + 1;
end;

Write (AddrFile, AddrRec);

{3.Переходим к началу только что измененной/добавленной записи}
if (FileSize (AddrFile) > 0) then begin
Seek (AddrFile, curPos);
NewRec:= False;
end;

EnableButtons (True);
btnUpdate.Caption:= '&Записать';
end;


{«Удаление« записи. Фактически происходит чтение всех записей, расположенных
в файле после удаляемой записи с последующим «урезанием« файла, начиная
с номера удаляемой записи. После этого в файл дописываются те записи, которые
следовали за удаляемой}
procedure TFormMain.DeleteRec;
var curPos, numRecs, i: Integer;
RecBuf: Array [0..MAXRECS] of TAddressRec;
begin
if MessageDlg ('Вы уверены, что хотите удалить текущую запись?',
mtConfirmation, [mbYes, mbNo], 0) = mrNo then Exit;

{Если пользователь нажал кнопку «Удалить« в процессе добавления новой записи —
просто читаем текущую запись из файла и выходим из процедуры}
if NewRec then begin
ReadRec;
NewRec:= False;
EnableButtons (True);
Exit;
end;

{Получаем текущую позицию в файле}
curPos:= FilePos (AddrFile);
{Получаем количество записей, которые необходимо будет поместить в буфер}
numRecs:= FileSize (AddrFile) — curPos — 1;

{Теперь, если мы находимся не на последней записи...}
if (FilePos (AddrFile) < (FileSize (AddrFile) — 1)) then begin
{...переходим на позицию в файле, следующую за удаляемой записью}
Seek (AddrFile, FilePos (AddrFile) + 1);
i:= 0;
{Читаем текущую запись в память}
while not eof (AddrFile) do begin
Read (AddrFile, RecBuf [i]);
Inc (i);
end;

{Возращаемся на удаляемую запись и «обрезаем« файл, начиная
с текущей позиции}
Seek (AddrFile, curPos);
Truncate (AddrFile);

{После этого записываем предварительно скопированные записи в файл}
for I:= 0 to numRecs — 1 do
Write (AddrFile, RecBuf [I]);
end
else begin
{...иначе просто удаляем последнюю запись}
Truncate (AddrFile);
{Поскольку удаляемая запись была последней в файле, номер текущей записи
следует уменьшить на 1}
Dec (curPos);
end;
{Возвращаемся на запись, которая была расположена перед удаленной}
if curPos>-1 then begin
Seek (AddrFile, curPos);
ReadRec;
end
else
EmptyEdits;
end;

procedure TFormMain.btnDeleteClick (Sender: TObject);
begin
DeleteRec;
end;

{=============Процедуры и методы навигации==============}
{Переход на другую запись в зависимости от указанного типа перемещения}
procedure TFormMain.MoveToRec (Direction: TRecMove);
var pos: Integer;
begin
EnableButtons (True);
{Получаем текущую позицию в файле}
pos:= FilePos (AddrFile);
{Если файл пуст — выходим из процедуры}
if (FileSize (AddrFile) = 0) then Exit;

{В зависимости от типа перемещения устанавливаем номер записи, на которую
следует перейти}
case Direction of
recFirst : pos:= 0;
recLast : pos:= FileSize (AddrFile) — 1;
recNext : if (FilePos (AddrFile) < (FileSize (AddrFile) — 1)) then
pos:= FilePos (AddrFile) + 1
else
Exit;
recPrev : if (FilePos (AddrFile) > 0) then
pos:= FilePos (AddrFile) — 1
else
Exit;
end;
Seek (AddrFile, pos);
ReadRec;
NewRec:= False;
end;

procedure TFormMain.LocateRec (Value: string; FromBOF: Boolean);
var curPos, SearchPos: Integer;
Found: Boolean;
begin
{Сохраняем текущую позицию в файле}
curPos:= FilePos (AddrFile);
{Устанавливаем позицию, с которой начнем искать — текущей или с начала файла}
if FromBOF then
SearchPos:= 0
else
SearchPos:= curPos + 1;

Found:= False;
while (SearchPos <= (FileSize (AddrFile) — 1)) AND (NOT Found) do begin
{Перемещаемся на позицию, с которой начнется поиск}
Seek (AddrFile, SearchPos);
Read (AddrFile, AddrRec);
if (AddrRec.LastName = Value) then begin
{Если запись соответствует параметру поиска}
Found:= True;
{Подаем звуковой сигнал}
MessageBeep (MB_OK);
{Теперь нам следует вернуться на запись назад, потому что при чтении
данных из файла мы автоматически переместились вперед}
Seek (AddrFile, SearchPos);
ReadRec;
end;
Inc (SearchPos)
end;

if not Found then
ShowMessage ('Запись с указанной фамилией не найдена');
end;

{Подготавливаем форму для ввода новой записи}
procedure TFormMain.btnNewClick (Sender: TObject);
begin
CreateNewRec;
end;

procedure TFormMain.btnUpdateClick (Sender: TObject);
begin
UpdateRec;
end;

{Перемещаем указатель на первую позицию в файле и загружаем запись}
procedure TFormMain.btnFirstClick (Sender: TObject);
begin
MoveToRec (recFirst);
end;

{Перемещаем указатель на последнюю позицию в файле и загружаем запись}
procedure TFormMain.btnLastClick (Sender: TObject);
begin
MoveToRec (recLast);
end;

{Перемещаем указатель на следующую позицию в файле и загружаем запись}
procedure TFormMain.btnNextClick (Sender: TObject);
begin
MoveToRec (recNext);
end;

{Перемещаем указатель на предыдущую позицию в файле и загружаем запись}
procedure TFormMain.btnPrevClick (Sender: TObject);
begin
MoveToRec (recPrev);
end;

procedure TFormMain.btnFindClick (Sender: TObject);
begin
if (EditSearchByLastName.Text <> '') then begin
if NewRec then
btnUpdateClick (Self);
LocateRec (EditSearchByLastName.Text, True);
end;
end;

procedure TFormMain.btnFindNextClick (Sender: TObject);
begin
LocateRec (EditSearchByLastName.Text, False);
end;

procedure TFormMain.btnInsertClick (Sender: TObject);
begin
InsertRec;
end;

procedure TFormMain.EmptyEdits;
var i:Integer;
begin
for i:= 0 to ComponentCount — 1 do
if (Components [i] is TEdit) then
TEdit (Components [i]).Clear;
end;

end.

Сергей Кривошеев,
sergk@mail.spbnit.ru


Задать вопрос
Прислать свою статью для публикации в журнале
Просто поговорить
Получить именной бланк подписки на "бумажную" версию

До следующего выпуска!
Елена Полонская, редактор "К+П"
www.comizdat.com

Перепечатка материалов этой рассылки разрешается только по согласованию с редакцией журнала "Компьютеры+Программы"



http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу

В избранное