Программирование arrow Delphi arrow База данных методами Delphi

База данных методами Delphi

В статье рассматривается работа с бинарными файлами из Delphi, а так же использование Object Pascal для управления записью, чтением и изменением собственных типов файлов.

Постановка задачи: Допустим, мне нужно в приложении Delphi сохранять некоторую информацию на диск. Мне не охото работать с текстовыми файлами, так как просмотр и обновление информации в них довольно муторное занятие. Преобладать будут операции записи и чтения, в то время как операции изменения и апдейта будут присутствовать в меньшей степени. Вся информация будет хранится в переопределённом типе данных Pascal Record. Итак, какой подход мне лучше всего использовать?

BDE плюс Paradox или Access, ... спасибо, не надо...Не хотелось бы испытывать мороку с BDE. Использовать текстовые файлы ASCII ? Не пойдёт. Нужна хоть какая-то минимальная защита, а текстовые файлы "полностью видимы". Оказывается, ответ на данный вопрос кроется в Delphi, а именно в непечатных файлах (или файлы некоторых типов/бинарные файлы).

Файлы

В Delphi существует три класса файлов: typed, text, и untyped. Файлы typed - это файлы, которые содержат данные определённого типа, такие как Double, Integer или предварительно определённый тип Record. Текстовые файлы содержат читаемые символы ASCII. Файлы Untyped используются в том случае, если мы хотим работать с файлом через определённую структуру.

Файлы Typed

В отличие от тектовых файлов, которые содержат строки, завершающиеся комбинацией CR/LF, файлы typed содержат данные, взятые из определённой структуры данных.

Например, следующее объявление создаёт запись с именем TMember и массив переменных типа TMember, который мы будем использовать для хранения нашей информации.

 type
  TMember = record
    name  : string[50];
    eMail : string[30];
    Posts : LongInt;
end;

var
  Members: array[1..50] of TMember;

Перед тем, как мы сможем записать информацию на диск, нам необходимо объявить переменную типа file. Следующая строка объявляет переменную файла F:

var
  F: file of TMember;

Обратите внимание:

Чтобы создать файл typed в Delphi, мы используем следующий синтакс:
var
  SomeTypedFile: file of SomeType;

 

Базовый тип (SomeType) для файла может быть скалярным (наподобие Double), массивом или записью. Он не может быть длинной строкой, динамическим массивом, классом, объектом или указателем.

Чтобы начать работать с файлом из Delphi нам надо связать файл на диске с переменной файла в нашей программе. Для этого используем процедуру AssignFile.

AssignFile(F, 'Members.dat');

Как только связь с внешним файлом установлена, переменную F необходимо 'открыть' для подготовки её к чтению или записи. Для открытия существующего файла мы используем процедуру Reset либо Rewrite для создания нового файла. После того, как программа закончит обработку файла, его необходимо закрыть при помощи процедуры CloseFile. Сразу после закрытия файла, связанный с ним внешний файл будет обновлён. Затем переменную файла можно связать с другим внешним файлом. Вообще, мы должны всегда производить обработку исключительных ситуаций, так как при работе с файлами может происходить довольно много ошибок. Например, если мы вызовем CloseFile для файла, который уже закрыт, то Delphi выдаст ошибку I/O. С другой стороны, если мы попробуем закрыть файл, до вызова AssignFile, то результаты могут быть непредсказуемыми.

Запись

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

var
  F: file of TMember;
begin
  AssignFile(F,'members.dat');
  Rewrite(F);
  try
    for i:= 1 to 50 do
      write (F, Members[i]);
  finally
    CloseFile(F);
  end;
end;

Чтение

Для получения всей информации из файла 'members.dat' используется следующий код:

var
  Member: TMember
  F: file of TMember;
begin
  AssignFile(F,'members.dat');
  Reset(F);
  try
    while not Eof(F) do
    begin
      read (F, Member);
       { Что-нибудь делаем с данными }
    end;
  finally
    CloseFile(F);
  end;
end;

Обратите внимание:

Eof это функция проверки конца файла (EndOfFile). Мы используем эту функцию, чтобы не выйти за пределы файла (за пределы последней, сохранённой записи).

Поиск и позиционирование

Обычно, доступ к файлам осуществляется последовательно. При чтении из файла (используя стандартную процедуру Read) или при записи (используя стандартную процедуру Write), текущая позиция в файле перемещается на следующий по порядку компонент (следующая запись). К файлам typed так же можно обращаться через стандартную процедуру Seek, которая перемещает текущую позицию в файле на указанный компонент. Для определения текущей позиции в файле и размера файла можно использовать функции FilePos и FileSize.

 {устанавливаем на начало - на первую запись}
Seek(F, 0);
{устанавливаем на 5-ю запись}
Seek(F, 5);
{Переходим в конец - "после" последней записи}
Seek(F, FileSize(F)); 

Изменение и обновление

Мы разобрались как записывать и считывать из файла массив Members. А что, если нам нужно найти десятую запись и изменить в ней e-mail? Давайте посмотрим на процедуру, которая делает это:

 procedure ChangeEMail(const RecN: integer; const NewEMail: string);
var
  DummyMember: TMember;
begin
   {связывание, открытие, блок обработки исключений}
  Seek(F, RecN);
  read(F, DummyMember);
  DummyMember.Email := NewEMail;
   {чтение перемещается на следующую запись, для этого необходимо
  вернуться на первоначальную запись, а затем записать}
  Seek(F, RecN);
  write(F, DummyMember);
   {закрываем файл}
end;

Всё готово

Итак, теперь мы имеем всё, что нам нужно для реализации нашей задачи. Мы можем записать информацию на диск, считать её, и даже изменить некоторые данные (например, e-mail) в "середине" файла. Самое главное, что этот файл не в ASCII формате !

Общий вид модуля выглядит примерно так [здесь для наглядности данные выводятся в StringGrid'e]:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls, Grids;

type
  TMember = record
    name  : string[50];
    eMail : string[30];
    Posts : LongInt;
end;

type
  TForm1 = class(TForm)
    SaveBtn: TButton;
    EnterDataToArrayBtn: TButton;
    OpenBtn: TButton;
    StringGrid1: TStringGrid;
    ChangeEmailBtn: TButton;
    procedure SaveBtnClick(Sender: TObject);
    procedure EnterDataToArrayBtnClick(Sender: TObject);
    procedure OpenBtnClick(Sender: TObject);
    procedure ChangeEmailBtnClick(Sender: TObject);
  private
     { Private declarations }
  public
     { Public declarations }
end;

var
  Form1: TForm1;
  Members : array[1..50] of TMember;

implementation
 {$R *.DFM}

procedure TForm1.SaveBtnClick(Sender: TObject);
var
  F : file of TMember;
  i: integer;
begin
  AssignFile(F,'members.dat');
  Rewrite(F);
  try
    for i:= 1 to 50 do
      write (F, Members[i]);
  finally
    CloseFile(F);
  end;
end;

procedure TForm1.EnterDataToArrayBtnClick(Sender: TObject);
begin
  Members[1].name:='___Nikolay';
  Members[1].eMail:=' Этот e-mail защищен от спам-ботов. Для его просмотра в вашем браузере должна быть включена поддержка Java-script ';
  Members[1].Posts:=10;
  Members[2].name:='Sveta';
  Members[2].eMail:=' Этот e-mail защищен от спам-ботов. Для его просмотра в вашем браузере должна быть включена поддержка Java-script ';
  Members[2].Posts:=28;
  Members[3].name:='Elena';
  Members[3].eMail:=' Этот e-mail защищен от спам-ботов. Для его просмотра в вашем браузере должна быть включена поддержка Java-script ';
  Members[3].Posts:=5;
end;

procedure TForm1.OpenBtnClick(Sender: TObject);
var
  ReadMembers: array[1..50] of TMember;
  F: file of TMember;
  i: integer;
begin
  AssignFile(F,'members.dat');
  Reset(F);
  try
    i:=0;
    while not Eof(F) do
    begin
      read (F, ReadMembers[i]);
       ////////////////////////
      StringGrid1.Cells[0,i]:=ReadMembers[i].name;
      StringGrid1.Cells[1,i]:=ReadMembers[i].eMail;
      StringGrid1.Cells[2,i]:=IntToStr(ReadMembers[i].Posts);
       ////////////////////////
      i:=i+1;
    end;
  finally
    CloseFile(F);
  end;
end;

procedure ChangeEMail (const RecN : integer; const NewEMail : string);
var
  DummyMember: TMember;
  F: file of TMember;
begin
  AssignFile(F,ExtractFilePath(Application.ExeName)+'members.dat');
  Reset(F);
  Seek(F, RecN);
  read(F, DummyMember);
  DummyMember.Email := NewEMail;
   {чтение перемещается на следующую запись, для этого необходимо
  вернуться на первоначальную запись, а затем записать}
  Seek(F, RecN);
  write(F, DummyMember);
  CloseFile(F);
end;

procedure TForm1.ChangeEmailBtnClick(Sender: TObject);
begin
  ChangeEmail(2, ' Этот e-mail защищен от спам-ботов. Для его просмотра в вашем браузере должна быть включена поддержка Java-script ');
end;

end.
 
« Предыдущая статья   Следующая статья »


  • Delphi, Написание внешних компонент для 1С на Delphi
    Насколько мне известно, многие 1С-ники хотели бы изучить написание внешних компонент, чтобы поднять свое магическое искусство 1С на качественно иную ступень. Что этому может помешать? Во-первых, известный синдром компонентофобии (который исторически берет свое начало от криво написанных внешних компонент). Во-вторых –синдром  клинически запутанного кода. OLE-программирование – это не самая простая штука, и, как говорится, «не всякая птица долетит до середины Днепра&...
  • Delphi, Вывод графиков функций в Delphi
    Изучая доступную литературу по программированию, которую я нашел в Интернете, а также некоторые программы, я пришел к выводу, что программисты то ли не осознают, то ли не хотят напрягаться на эту тему, и всё делают, как в школе учили. Строят графики, как на бумаге. Тем самым умаляя возможности компьютера. Оставляя те же недостатки метода построения, и даже усугубляя их....
  • Delphi, Приемы работы с базами данных в Delphi
    Данная статья предназначена в основном для тех, кто начинает работать с базами данных. Здесь собраны приемы, направленные на оптимизацию и ускорение работы с базами данных. Описанные примеры являются результатом многолетней работы автора с СУБД MS SQL Server, Oracle и Access. Примеры описываются в общем виде, без привязки к какой-либо конкретной СУБД....
  • Delphi, Работа с потоками в Delphi
    Данная статья предназначена для начинающих программистов, которые никогда не работали с потоками, и хотели бы узнать основы работы с ними. Желательно, чтоб читатель знал основы ООП и имел какой-нибудь опыт работы в Delphi. Для начала давайте определимся, что под словом "поток" я подразумеваю именно Thread, который еще имеет название "нить"....
  • Delphi, Message методы, или обработка сообщений классами в Delphi
    Данная статья предназначения для более глубокого понимания того, как реализована обработка сообщений Windows в VCL и как это можно и нужно использовать в своих целях и использовать правильно....
  • Delphi, Запись CD-DVD дисков в Delphi
    Доброго времени суток уважаемые любители Delphi. В этой статье я расскажу про запись CD\DVD дисков в среде Delphi. Общие принципы, изложенные в этой статье подойдут не только для языка Delphi, но и для языка С++. Для прочтения этой статьи с максимальной пользой, читателю рекомендуется получить базовые понятия об OLE\COM, впрочем даже незнание этих понятий вряд ли помешает понимаю этой статьи, так как классы и компоненты Delphi (так же как и классы С++), которые мы будет использовать полностью ск...
  • Delphi, Хуки в Windows на Delphi
    Тема про хуки является популярной на многих форумах программистов. Материал этих статей рассчитан на начинающего пользователя, примеры будут на Delphi. В этой статье будут изложены основные принципы механизма хуков, и будет написан пример клавиатурного шпиона....
  • Delphi, Перехват API функций в Delphi с помощью сплайсинга
    Сегодня я расскажу довольно таки эффективную методику перехвата API функций. Не следует думать, что если мы хотим перехватить API функции, то мы пишем либо троян, вирус и ещё какую-нибудь заразу, с помощью перехвата API функций осуществляются многие защитные механизмы, перехват API функций это довольно-таки нужная и полезная вещь. Для прочтения данной статьи с максимальной пользой обязательны, нужны, как минимум, начальные знания низкоуровневого программирования и хотя бы какие-нибудь знания арх...