1. Обзор
При написании программ баз данных на Delphi часто используются операции импорта и экспорта данных, такие как: сохранение данных в большой базе данных в виде переносимых файлов для удобного чтения извне, импорт информации, хранящейся в файлах, в другую базу данных; База данных в виде файлов данных упрощает обмен данными внутри программ и между ними и позволяет избежать громоздких шагов по обмену данными через память. Например, в программе общего отчета, написанной автором, этот элемент управления используется в качестве носителя информации. доставка.
2. Основные идеи
В качестве элемента управления хранилищем дейтаграмм он должен иметь возможность хранить и считывать основную информацию набора данных (например, имя поля, отображаемое имя поля, тип данных поля, количество записей, количество полей, текущее значение указанного поля). в указанной записи и т. д.), и должен обеспечивать лучшие характеристики упаковки для удобства использования.
Основываясь на этом, автор использовал объектно-ориентированные характеристики Delphi5.0 для проектирования и разработки системы управления хранением дейтаграмм.
3. Способ реализации
Напишите следующий блок кода:
модуль IbDbFile;
интерфейс
Использует Windows, SysUtils, классы, формы, базы данных, DbTables, диалоги;
Конст
Флаг = 'Студия программного обеспечения для обработки датаграмм';
Тип
TDsException = Класс (Исключение);
TIbStorage = класс (TComponent)
Частный
FRptTitle: string; //Описание датаграммы хранилища
FPageHead: строка; //Описание заголовка страницы.
FPageFoot: string; //Описание стоп;
FFieldNames: TStrings; //Таблица имен полей;
FStreamIndex: TStrings // индекс поля;
FStream: TStream; //Поток, в котором хранится содержимое поля
FFieldCount: Integer; //Количество полей;
FRecordCount: Integer; //Количество записей;
FOpenFlag: Boolean // Флаг создания потока;
защищенный
процедура Reset; //Reset ---очистить содержимое потока;
процедура SaveHead(ADataSet: TDataSet; Fp: TStream); //Информация заголовка отчета о хранилище);
процедура LoadTableToStream(ADataSet: TDataSet); //Данные записи хранилища;
процедура IndexFields(ADataSet: TDataSet); //Сохраняем имена полей набора данных в список;
процедура GetHead(Fp: TFileStream); //Сохраняем информацию заголовка отчета;
процедура GetIndex(Fp: TFileStream); //Создаем индекс потока записей;
процедура GetFieldNames(Fp: TFileStream); //Читаем таблицу имен полей из потока;
function GetFieldName(AIndex: Integer): строка //Получаем имя поля;
функция GetFieldDataType (AIndex: Integer): TFieldType;
function GetDisplayLabel(AIndex: Integer): string //Получаем отображаемое имя поля;
процедура SaveFieldToStream(AStream: TStream; AField: TField); //Сохраняем поле в поток;
функция GetFieldValue(ARecordNo, FieldNo: Integer): строка //Содержимое поля;
общественный
Конструктор Create (AOwner: TComponent);
Переопределение деструктора;
процедура Open; //Создаем поток для подготовки к сохранению данных;
процедура SaveToFile(ADataSet: TDataSet; AFileName: string); //Метод хранения;
процедура LoadFromFile(AFileName: string); //Загрузка данных;
процедура FieldStream (ARecordNo, FieldNo: Integer; var AStream: TStream);
свойство FieldNames[Index: Integer]: чтение строки GetFieldName; //имя поля;
свойство FieldDataTypes [Индекс: Целое число]: TFieldType прочитано GetFieldDataType;
свойство FieldDisplayLabels[Index: Integer]: чтение строки GetDisplayLabel;
свойство Fields[RecNo, FieldIndex: Integer]: чтение строки GetFieldValue;
//свойство FieldStreams[RecNo, FieldIndex: Integer]: TStream читает GetFieldStream;
свойство RecordCount: целое число, чтение FRecordCount, запись FRecordCount;
свойство FieldCount: Целочисленное чтение FFieldCount запись FFieldCount;
опубликовано
свойство RptTitle: чтение строки FRptTitle запись FRptTitle;
свойство PageHead: чтение строки FPageHead запись FPageHead;
свойство PageFoot: чтение строки FPageFoot запись FPageFoot;
конец;
функция ReadAChar(AStream: TStream): Char;
функция ReadASt(AStream: TStream): строка;
функция ReadBStr(AStream: TStream; Размер: целое число): строка;
функция ReadAInteger(AStream: TStream): Integer;
процедура WriteASt(AStream: TStream; AStr: строка);
процедура WriteBStr(AStream: TStream; AStr: строка);
процедура WriteAInteger(AStream: TStream; AInteger: Integer);
процедура Регистр;
выполнение
процедура Регистр;
начинать
RegisterComponents('Доступ к данным', [TIbStorage]);
конец;
функция ReadAChar(AStream: TStream): Char;
Вар
АЧар: Чар;
начинать
AStream.Read(AChar, 1);
Результат: = AChar;
конец;
функция ReadASt(AStream: TStream): строка;
вар
Стр: Строка;
C:Чар;
начинать
Стр := '';
C := ReadAChar(AStream);
Пока C <> #0 делайте
начинать
Стр := Стр + С;
C := ReadAChar(AStream);
конец;
Результат: = Стр;
конец;
функция ReadBStr(AStream: TStream; Размер: целое число): строка;
вар
Стр: Строка;
C:Чар;
Я: целое число;
начинать
Стр := '';
Для I := 1 к размеру do
начинать
C := ReadAChar(AStream);
Стр := Стр + С;
конец;
Результат: = Стр;
конец;
функция ReadAInteger(AStream: TStream): Integer;
вар
Стр: Строка;
C:Чар;
начинать
Результат: = МаксИнт;
Стр := '';
C := ReadAChar(AStream);
Пока C <> #0 делайте
начинать
Стр := Стр + С;
C := ReadAChar(AStream);
конец;
пытаться
Результат: = StrToInt(Str);
кроме
application.MessageBox('Текущая строка не может быть преобразована в целое число!', 'Ошибка',
Mb_Ok + Mb_IconError);
конец;
конец;
процедура WriteASt(AStream: TStream; AStr: строка);
начинать
AStream.Write(Указатель(АСтр)^, Длина(АСтр) + 1);
конец;
процедура WriteBStr(AStream: TStream; AStr: строка);
начинать
AStream.Write(Указатель(АСтр)^, Длина(АСтр));
конец;
процедура WriteAInteger(AStream: TStream; AInteger: Integer);
вар
С: строка;
начинать
S := IntToStr(AInteger);
WriteAstr(AStream, S);
конец;
Конструктор TIbStorage.Create(AOwner: TComponent);
начинать
унаследовано Create(AOwner);
FOpenFlag := False; // Флаг, определяющий, создан ли поток
конец;
Деструктор TIbStorage.Destroy;
начинать
если FOpenFlag, то
начинать
FStream.Бесплатно;
FStreamIndex.Free;
FFieldNames.Free;
конец;
унаследовал Уничтожить;
конец;
процедура TIbStorage.Open;
начинать
FOpenFlag := Истина;
FStream := TMemoryStream.Create;
FStreamIndex:= TStringList.Create;
FFieldNames := TStringList.Create;
Перезагрузить;
конец;
процедура TIbStorage.Reset //сброс;
начинать
если FOpenFlag тогда
начинать
FFieldNames.Очистить;
FStreamIndex.Очистить;
FStream.Size:= 0;
FRptTitle := '';
FPageHead := '';
FPageFoot := '';
ФФиелдКаунт: = 0;
ФРекордКаунт: = 0;
конец;
конец;
//-------сохраним часть данных
процедура TIbStorage.SaveToFile(ADataSet: TDataSet; AFileName: строка);
вар
Фп: TFileStream;
Я: целое число;
Ч: Чар;
Т1, Т2: ТДатетеВремя;
Стр: строка;
начинать
если Не FOpenFlag, то
начинать
showmessage('Объект не открыт');
Выход;
конец;
пытаться
если FileExists(AFileName), то DeleteFile(AFileName);
Fp := TFileStream.Create(AFileName, fmCreate);
Перезагрузить;
SaveHead(ADataSet, Fp); //Сохраняем информацию заголовка ---Дополнительные инструкции
IndexFields(ADataSet); //Сохраняем информацию о поле набора данных в FFieldName;
LoadTableToStream(ADataSet); //Сохраняем информацию о наборе данных
WriteASr(Fp, FFieldNames.Text); //Информация об имени поля хранения.
Ч := '@';
Fp.Write(Ch, 1);
WriteASt(Fp, FStreamIndex.Text); //Список индексов полей хранения;
Ч := '@';
Fp.Write(Ch, 1);
Fp.CopyFrom(FStream, 0);
окончательно
Фп.Бесплатно;
конец;
конец;
процедура TIbStorage.SaveHead(ADataSet: TDataSet; Fp: TStream);
Вар
Я: целое число;
Ч: Чар;
начинать
если Не ADataSet.Active, то ADataSet.Active := True;
WriteASt(Fp, Флаг);
WriteASt(Fp, FRptTitle);
WriteASt(Fp, FPageHead);
WriteASt(Fp, FPageFoot);
FFieldCount := ADataSet.Fields.Count;
FRecordCount := ADataSet.RecordCount;
WriteASt(Fp, IntToStr(ADataSet.Fields.Count));
WriteASt(Fp, IntToStr(ADataSet.RecordCount));
Ч := '@';
Fp.Write(Ch, 1);
конец;
процедура TIbStorage.IndexFields(ADataSet: TDataSet);
вар
Я: целое число;
Поле: TField;
начинать
Для I := 0 до ADataSet.Fields.Count - 1 сделать
начинать
AField := ADataSet.Fields[I];
//Не использовать FFieldNames.Values[AField.FieldName] := AField.DisplayLabel, чтобы учитывать эффективность;
FFieldNames.Add(AField.FieldName + '=' + AField.DisplayLabel);
FFieldNames.Add(AField.FieldName + 'DataType=' + IntToStr(Ord(AField.DataType)));
конец;
конец;
процедура TIbStorage.LoadTableToStream(ADataSet: TDataSet);
вар
Нет: целое число;
I, J, Размер: Целое число;
Tmp, Id, Str: строка //id=string(RecNO) + string(FieldNo)
Лен: целое число;
Ч: Чар;
Блобстрим: ТБлобстрим;
начинать
если Не FOpenFlag, то
начинать
showmessage('Объект не открыт');
Выход;
конец;
пытаться
Адатасет.дисаблеконтролс;
ADataSet.First;
Нет := 0;
FStreamIndex.Очистить;
FStream.Size:= 0;
Пока это не ADataSet.Eof, сделайте
начинать
Нет := Нет + 1;
Для J := 0 до ADataSet.Fields.Count - 1 сделайте
начинать
Идентификатор: = Inttostr(NO) + '_' + IntToStr(J);
//Создаем индекс позиции потока, индекс указывает на: Size#0Content
FStreamIndex.Add(Id + '=' + IntToStr(FStream.Position));
//Сохраняем информацию о поле в поток
SaveFieldToStream(FStream, ADataSet.Fields[J]);
конец;
АDataSet.Next;
конец;
окончательно
Адатасет.энаблеконтролс;
конец;
конец;
//Если текущее содержимое поля пусто или BlobSize<=0, только размер поля равен 0, а содержимое не записывается.
процедура TIbStorage.SaveFieldToStream(AStream: TStream; AField: TField);
вар
Размер: Целое число;
Ч: Чар;
XF: ТСтрим;
Стр: строка;
начинать
если AField.IsBlob, то
начинать
//Как сохранить содержимое поля TBlobField в виде потока
Xf := TBlobStream.Create(TBlobField(AField), bmread);
пытаться
если Xf.Size > 0, то
начинать
Размер := Xf.Размер;
WriteAInteger(AStream, Размер);
AStream.CopyFrom(Xf, Xf.Size);
конец
еще
WriteAInteger(AStream, 0);
окончательно
XF.Бесплатно;
конец;
конец
еще
начинать
Стр := AField.AsString;
Размер: = Длина (Str);
WriteAInteger(AStream, Размер);
если Размер <> 0, то
AStream.Write(Указатель(Str)^, Размер);
//WriteAstr(AStream, Str);
конец;
Ч := '@';
AStream.Write(Ch, 1);
конец;
//------------Загрузим данные
процедура TIbStorage.LoadFromFile(AFileName: строка);
вар
Фп: TFileStream;
Проверка: строка;
начинать
Перезагрузить;
пытаться
если Не FileExists(AFileName), то
начинать
showmessage('Файл не существует:' + AFileName);
Выход;
конец;
Fp := TFileStream.Create(AFileName, fmOpenRead);
Проверьте: = ReadAStr(Fp);
если Проверить <> Флаг, то
начинать
Application.MessageBox('Недопустимый формат файла', 'Ошибка', Mb_Ok + Mb_IconError);
Выход;
конец;
GetHead(Фп);
GetFieldNames(Fp);
ПолучитьИндекс(Фп);
FStream.CopyFrom(Fp, Fp.Size-Fp.Position);
окончательно
Фп.Бесплатно;
конец;
конец;
процедура TIbStorage.GetHead(Fp: TFileStream);
начинать
FRptTitle := ReadASt(Fp);
FPageHead:= ReadAstr(Fp);
FPageFoot:= ReadAstr(Fp);
FFieldCount := ReadAInteger(Fp);
FRecordCount := ReadAInteger(Fp);
если ReadAChar(Fp) <> '@' then showmessage('Ошибка файла GetHead');
конец;
процедура TIbStorage.GetFieldNames(Fp: TFileStream);
вар
Ч: Чар;
Стр: строка;
начинать
Стр := '';
Стр := ReadAStr(Fp);
FFieldNames.CommaText := Str;
Ch := ReadAChar(Fp);
if Ch <> '@' then Showmessage('Ошибка при получении имен полей');
конец;
процедура TIbStorage.GetIndex(Fp: TFileStream);
вар
Ч: Чар;
Стр: строка;
начинать
Стр := '';
Стр := ReadAStr(Fp);
FStreamIndex.CommaText := Str;
Ch := ReadAChar(Fp);
if Ch <> '@' then Showmessage('При получении ошибки индекса позиции поля');
конец;
//---------Читаем часть значения поля
функция TIbStorage.GetFieldValue(ARecordNo, FieldNo: Integer): строка;
вар
Идентификатор, Т: строка;
Поз.: целое число;
Лен, я: целое число;
Er: логическое значение;
начинать
Результат := '';
Эр := Ложь;
если ARecordNo > FRecordCount, то
Эр: = правда; //ARecordNo: = FRecordCount;
если ARecordNo < 1, то
Эр := Истина // ARecordNo := 1;
если FieldNo >= FFieldCount, то
Er := True // Номер поля := FFieldCount - 1;
еслиFieldNo < 0 тогда
Эр := Истина //Номер поля := 0;
если Эр, то
начинать
Showmessage('Номер записи или метка поля находится за пределами границ');
Выход;
конец;
если FFieldCount = 0, то выйти;
Идентификатор: = Inttostr(ARecordNO) + '_' + IntToStr(FieldNo);
Pos := StrToInt(FStreamIndex.Values[Id]);
FStream.Position := Позиция;
//Получаем длину содержимого поля
Лен: = ReadAInteger(FStream);
если Len > 0, то
Результат: = ReadBStr(FStream, Len);
если ReadAChar(FStream) <> '@', то
Showmessage('При чтении поля обнаружена ошибка формата сохранения');
конец;
процедура TIbStorage.FieldStream(ARecordNo, FieldNo: Integer; var AStream: TStream);
вар
Идентификатор, Т: строка;
Поз.: целое число;
Лен, я: целое число;
Er: логическое значение;
начинать
Эр := Ложь;
если ARecordNo > FRecordCount, то
Эр: = правда; //ARecordNo: = FRecordCount;
если ARecordNo < 1, то
Эр := Истина // ARecordNo := 1;
если FieldNo >= FFieldCount, то
Er := True // Номер поля := FFieldCount - 1;
еслиFieldNo < 0 тогда
Эр := Истина //Номер поля := 0;
если Эр, то
начинать
TDsException.Create('Индекс индекса функции GetFieldValue выходит за пределы');
Выход;
конец;
если FFieldCount = 0, то выйти;
Идентификатор: = Inttostr(ARecordNO) + IntToStr(FieldNo);
Pos := StrToInt(FStreamIndex.Values[Id]);
FStream.Position := Позиция;
Лен: = ReadAInteger(FStream);
AStream.CopyFrom(FStream, Len);
конец;
function TIbStorage.GetFieldName(AIndex: Integer): строка //Получаем имя поля;
начинать
//Сохраняемые поля и типы данных составляют половину каждого
если ((AIndex < 0) или (AIndex >= FFieldNames.Count div 2)) то
Application.MessageBox('Индекс имени поля выходит за пределы', 'Ошибка программы',
Mb_Ok + Mb_IconError)
еще
Результат:= FFieldNames.Names[AIndex*2];
конец;
function TIbStorage.GetFieldDataType(AIndex: Integer): TFieldType; //Получаем имя поля;
начинать
//Сохраняемые поля и типы данных составляют половину каждого
если ((AIndex < 0) или (AIndex >= FFieldNames.Count div 2)) то
Application.MessageBox('Индекс типа данных поля выходит за пределы', 'Ошибка программы',
Mb_Ok + Mb_IconError)
еще
Результат:= TFieldType(StrToInt(FFieldNames.Values[FFieldNames.Names[AIndex*2+1]]));
конец;
function TIbStorage.GetDisplayLabel(AIndex: Integer): string //Получаем отображаемое имя поля;
начинать
если ((AIndex < 0) или (AIndex >= FFieldNames.Count)) то
Application.MessageBox('Индекс имени поля выходит за пределы', 'Ошибка программы',
Mb_Ok + Mb_IconError)
еще
Результат: = FFieldNames.Values[GetFieldName(AIndex)];
конец;
конец.
Благодаря тестированию этот элемент управления имеет хорошую поддержку часто используемых элементов управления наборами данных, таких как Ttable, Tquery, TaodTable, TadoQuery, TibTable, TibQuery и т. д., и имеет хорошую эффективность (тест: 1100 записей о персонале, 23 поля, хранящиеся в виде файла, занимают около 2 секунды).
4. Базовое использование элементов управления
1. Сохранение данных из набора данных в файл
IbStorage1.Open; //Создаем поток хранения
IbStorage1.SaveToFile(AdataSet, Имя_файла);
2. Чтение данных данных из файла
IbStorage1.Открыть;
IbStorage1.LoadFromFile(ИмяФайла);
3. Доступ к данным в хранилище дейтаграмм.
Значение := IbStorage1.Fields[ArecNo, AfieldNo] //Тип строки;
Другие опущены.
5. Заключение
При написании этого управления хранением дейтаграмм лучше решается проблема хранения и обмена данными в программах баз данных, а также обеспечивается практический контроль над разработкой программ баз данных.
Элемент управления прошел отладку в среде разработки Windows98 и Delphi5.