Информационный сервер для программистов: Исходники со всего света. Паскальные исходники со всего света
  Powered by Поисковый сервер Яndex: Найдется ВСЁ!
На Главную Pascal Форум Информер Страны мира
   Базы Данных    >>    swdbase
   
 
 SWdBase. Об'ектный интерфейс к DBF   Виктор Вагнер 11.03.1994

Объектно-ориентированный интерфейс к DBF-файлам с поддержкой MDX индексов



25k 
 

Группа SoftWeyr Объектно-ориентированный интерфейс к DBF-файлам версия 0.95 beta OVERVIEW Объектно-ориентированный интерфейс к DBF-файлам позволяет из программы, написанной на Turbo Pascal 6.0 и выше работать с DBF-файлами, пользуясь привычными понятиями языка xBase. (xBase - обобщенное название для dBase, FoxBase и Clipper) Основные преимущества: 1. Возможность открыть произвольное количество объектов баз данных (аналог рабочей области в xBase) 2. Возможность работы с базой данных, хранящейся не только в файле DOS, но и в EMS, в памяти или любом месте, к которому можно обратиться как к потоку с прямым доступом. 3. В отличие от большинства подобных продуктов поддерживаются фильтры (аналог SET FILTER TO) 4. Поддерживаются индексированные базы данных. 5. Объектно-ориентированная идеология позволяет легко добавлять новые типы индексов (которые у каждого xBase-совместимого продукта свои) и Memo-полей. Направления совершенствования (можете присоединяться) 1. Индексы других типов (модули dBaseIII.pas, dBaseIIIp.pas, dBaseIV.pas и Clipper.pas) 2. Поддержка .MDX файлов FoxPro 3. Поддержка вычисляемых индексных выражений 3. Объекты-наследники от TDBF, работающие с 3-й нормальной формой КОМПЛЕКТ ПОСТАВКИ Система поставляется в исходных текстах, поскольку разные версии TP имеют разный формат библиотек. Мы могли бы,конечно как делают некоторые, поставлять версии TPU для TP 6.0 и TP 7.0, но что будет делать бедный пользователь у которого окажется версия 7.5 или 8.0 и не окажется нашего адреса? Кроме того у Borland Pascal 7.0 три формата модулей - TPU,TPP и TPW (кстати, в Turbo Pascal for Windows 1.5 и 1.0 наша библиотека тоже должна работать, хотя мы и не пробовали). Итак, в комплект поставки входят: DBASE.PAS - модуль, содержащий объект TDBF и объекты, TIndex и TMemofile, реализующие абстрактную концепцию работы с индексами и файлами Memo. FOXPRO.PAS - реализация индексов для IDX-файлов FOXPRO и реализация Memo для FPT-файлов. Почему FoxPro? Да потому, что у нас было описание форматов для нее. Если кто напишет интерфейс к NDX и MDX файлам dBase или NTX Clipper, и положит его туда, где я смогу до него дотянуться, я буду только рад, да и все остальные пользователи - тоже. УСЛОВИЯ РАСПРОСТРАНЕНИЯ Как и вся продукция, поступающая на mailserver'ы под маркой SoftWeyr, данная библиотека не является ShareWare, она FreeWare, т. е. автор далек от мысли требовать какие-либо деньги даже с тех, кто использует ее для добывания скудного прокорма (а сколь скуден прокорм программиста в б.СССР автор знает по собственному опыту). С другой стороны, автору не понравится если вы будете 1. Удалять его CopyRight 2. Распространять библиотеку кусками (При распространении должны быть присутствовать по крайней мере три файла - DBASE.PAS, FOXPRO.PAS и DBOBJECT.DOC, хотя последний можно распространять и отдельно) Особо не приветствуется распространение библиотеки в откомпилированном виде без приложения исходных текстов 3. Вносить исправления и изменения так, что нельзя понять где авторский текст, а где нет. Желательно все доделки реализовывать в виде отдельных файлов, а если уж такой возможности нет, выделять комментарием начало и конец вашей вставки и оставлять в виде комментариев то, что вы удалили. И последнее, большая просьба - буде кто найдет, а тем паче исправит ошибку или сделает общественно полезную доделку - типа реализации другого формата индекса или MEMO, пришлите это мне. (но только не в виде TPU, пожалуйста, а в исходном виде.) Вообще, я надеюсь, что время от времени будут появляться новые версии, во всяком случае появится версия 1.00 не beta, так что распространяя доделки в виде TPU вы не только привяжете пользователей к определенной версии Turbo Pascal, но и к определенной версии dBase.tpu, что совсем некорректно, ибо пока она beta. ИЕРАРХИЯ ОБЪЕКТОВ +---------+ Objects.TPU | TObject | +---+-----+ +-------+------+-----------------+ - - - - - - - - - | - - - - - - -| - - - - - - - - | - - - - - +--+---+ +--+----+ +--+-------+ DBase.TPU |TDBF | | TIndex| | TMemoFile| +--+---+ +--+----+ +---+------+ +---+---------+ | | | TFilteredDBF|+ - |- - - - - - - - - | - - - -+ +---+---------+ +--+--+ +---+---+ +---+--------+ ||TIDX | | TFPT | | | TIndexedDBF| +-----+ +-------+ +------------+ |FOXPRO.TPU | ОПИСАНИЕ ОБЪЕКТОВ 1. TDBF - объект реализующий в программе базу данных. Многим пользователям будет достаточно одного этого объекта, так как часто программе не нужны ни фильтры, ни индексы, а нужно просто тупо и в лоб прочитать DBF-файл. Поля S:PStream - Указатель на поток, откуда берутся данные. Читать это поле вам может потребоваться только для того, чтобы узнать статус потока в случае возникновения ошибки HeaderSize - размер заголовка DbF-файла. Сам не знаю, почему это поле не private RecSize - физический размер записи (вместе с признаком удаления). Тоже по смыслу внутреннее дело объекта RecCount : LongInt - общее количество записей в файле. Установка фильтров и индексов с опцией UNIQUE на значение этого поля не влияет. FieldCount : Byte - количество полей в базе данных SkipDeleted : Boolean - если True, то включен режим SET DELETED OFF. Просьба не изменять напрямую. для этого есть процедура SetDeleted Mode:Byte - содержит код режима доступа к БД. Одно из трех dbOpen - база открыта на чтение/запись dbReadOnly - база открыта только на чтение dbCreate - база в процессе создания. доступны только операции добавления полей и создания. RecNo:LongInt - номер текущей записи (физической) Изменять методами Go, Skip и Next VersionID: Byte - номер версии DBF файла. Если он не равер 3, то файл имеет поля типа Memo и нужно создать соответствующий объект TMemoFile, если вы хотите иметь доступ ко всей информации в файле. Memo:PMemoFile - указатель на объект TMemoFile, связанный с данным DBF-файлом. Инициализируется путем выполнения присваивания после инициализации объекта, если открыт сущетсвующий файл и перед выполенением методов Create/CopyStru если создается новый файл, содержащий поля типа Memo Методы: constructor Init (St:PStream); Инициализирует объект. Единственный параметр - указатель на поток. Если поток открыт в режиме stOpenRead, Mode получит значение dbReadOnly, если поток пуст, dbCreate. В случае возникновения ошибок, код ошибки заносится в глобальную переменную dbError и вызывается Fail. Поэтому, если вы размещаете объект в динамической памяти, проверьте, не равен ли nil указатель на него. Если это так, то произошла ошибка и базу данных вы не открыли. (см. раздел Constructor error recovery в Turbo Pascal Programmers Guide) Доступ к структуре БД Function Field(n:Integer):String; - Все кто работал с dBase знают, эта функция возвращает имя поля по его номеру. Function FieldNo( FName:String):byte; - А это обратная операция. И если структура базы данных задано не жестко, она вам понадобится - ведь обращение к полям происходит по номеру, а не по имени. (если же структура задана жестко, рекомендую описать целые константы, соответствующие именам полей и равные их номерам. Это нагляднее). В случае, если поле с указанным именем не найдено, возвращает 0. Function FieldType(n:integer):Char; - Возвращает тип поля, т.е букву N,C,L или M. Function FieldLen(n:integer):byte; {------ Возвращает длину поля} Function FieldDec(n:integer):byte; {------ Возвращает число знаков после запятой. Для всех полей кроме N всегда возвращает 0 Чтение значений из базы данных. В данном разделе параметр n задает номер поля. Читать можно только поля текущей записи Function GetField(n:Integer):string; Возвращает строковое представление любого поля кроме Memo. Для Memo возвращает строковое представление адреса в Memo-файле Function GetIntField(n:integer):LongInt; Возвращает целое представление числового поля с Dec=0 или адрес поля MEMO в файле Function GetRealField(n:integer):Float; Возвращает вещественное представление числового поля тип Float равен Real, если вы откомпилировали dBase.pas с опцией N- и Extended,если с N+. Но пусть вас это не волнует. Он совместим по присваиванию с любым вещественным типом и вы можете работать с тем, который вам нравится. Модуль dbase нигде не требует параметров-переменных типа Float, а только параметры значения и возвращаемые результаты функций. Function GetBooleanField(n:integer):Boolean; Возвращает значение логического поля} Вызов любой из этих функций для поля неправильного типа приведет к тому, что в переменной dbError появится значение кода ошибки, а функция вернет 0, false или пустую строку. Специального интерфейса с полями даты нет. Их надо рассматривать как частный случай строковых полей. Можно было, конечно,написать кое-что на эту тему, но с одной стороны в Turbo Professional есть мощнейший модуль TPDate, где все на эту тему уже написано, а с другой - не хочется, чтобы наш модуль его требовал при компиляции - вдруг у кого-то нет, а этому кому-то поля даты и не нужны. Изменение полей БД. Procedure PutField(n:Integer;Value:String); Изменяет строковое поле Procedure PutIntField(n:Integer;Value:LongInt); Изменяет целое поле, т.е. поле Numeric с числом десятичных цифр равным 0,или заносит ссылку на запись в файле Memo в поле Memo Procedure PutRealField(n:Integer;Value:Float); изменяет любое числовое поле Procedure PutBooleanField(n:Integer;Value:Boolean); Изменяет логическое поле Изменения будут храниться в буфере до тех пор пока вы не уйдете из данной записи или не вызовите деструктор. Тогда они запишутся на диск. Procedure PutMemoField Эти процедуры могут возвращать ошибки в глобальной переменной DBError. Если код ошибки больше 0, то произошла серьезная неприятность и надо что-то делать, если меньше, то это мелочи - строка не уместилась в поле и была обрезана. Подробнее - см. в разделе "Коды ошибок". Навигация по базе данных Procedure Go(ARecNo:LongInt);virtual; переход к записи по физическому номеру. Если база данных фильтрована, то текущей становится первая удовлетворяющая фильтру запись после указанной. Вместо номера записи можно использовать константы Top и Bottom, что делает работу с TDBF еще более похожей на xBase. Go делает удаленную запись текущей независимо от значения SkipDeleted Procedure Skip(Count:LongInt);virtual; Перемещает указатель на Count записей (вперед или назад - зависит от знака Count). В режиме Set Deleted Off удаленные записи не считаются. Записи не удовлетворяющие фильтру не считаются. В индексированном файле перемещение происходит в порядке индекса. Procedure Next - то же, что Skip(1), только быстрее. Function Eof:Boolean;virtual; - возвращает True, если достигнут конец файла Удаление/восстановление Procedure Delete - помечает текущую запись как удаленную. Procedure Recall - снимает с текущей записи отметку для удаления. Function IsDeleted:Boolean - возвращает True, если текущая запись помечена для удаления. Procedure SetDeleted(On:Boolean); - переключает режимы Set Deleted On и Set Deleted Off. SetDeleted(True)= Set Deleted On SetDeleted(False)= Set deleted off Для физического удаления записей нужно перезаписать весь файл. Добавление записей На самом деле для ввода новой информации нет нужды в специальных методах - установите курсор на конец файла и записывайте данные. Новая запись создастся автоматически. Но если она не одна, то придется заводить новые записи явным образом. Procedure InsertBlank(Before:boolean);virtual; {------- Вставляет пустую запись после/перед текущей} Procedure AppendBlank; {------- Вставляет пустую запись в конец файла} procedure AppendFrom(P:PDBF); {------ Копирует в данный файл записи из другого. Копируются только те поля, которые совпадают по имени и типу. По длине и числу десятичных знаков они могут не совпадать и кода ошибки вы скорее всего при этом не получите, так как копирование следующей записи его затрет. А вполне может случиться так, что данные последней записи влезут в новые поля, а данные предпоследней- нет. Копирование производится с учетом текущего фильтра базы - источника, ее режима Deleted и порядка, заданного ее индексом. ПРОЧЕЕ Destructor Done;virtual; - сохраняет последние изменения, уничтножает все буферы, индексы и т.п., закрывает файл. Не забывайте вызвать! Procedure Zap; уничтожает все данные, оставляя только заголовок файла. резервной копии не создает. СОЗДАНИЕ ФАЙЛОВ Если вы пытаетесь открыть несуществующий файл, то объект TDBF создастся и его поле Mode будет иметь значение dbCreate. Для того, чтобы с базой данных можно было работать, нужно создать структуру файла и записать его заголовок. Простейший способ - скопировать структуру другого файла с помощью procedure CopyStru(P:PDBF); Этот метод скопирует описание всех полей и создаст заголовок, переведя базу в режим dbOpen. Eсли вам нужно создать базу данных с собственной структурой, то вам придется воспользоваться методами Procedure AddField(Name:String;Typ:char;LEn,dec:byte); который добавляет одно поле с указанными свойствами и Procedure Create; который записывает заголовок и переводит базу в режим dbOpen. Например, скопировать первые 10 полей базы P в базу B (P и B - указатели типа PDBF) можно так: p:=New(PDBF,Init(New(PDosStream,Init('OLDFILE.DBF', stOpenRead))); b:=New(PDBF,Init(New(PDosStream,Init('NewFile.DBF', stCreate))); With P^ do For i:=1 to 10 do B^.AddField(Field(i),FieldType(i),FieldLen(i), FieldDec(i)); B^.Create; B^.AppendFrom(P); Dispose(P,Done); Dispose(B,Done); Объект TFilteredDBF Этот объект реализует базу данных, в которой доступны не все записи, а только те, которые удовлетворяют некоторым условиям. Эти условия задаются с помощью пользовательской функции, описанной как function MyFilter(var D:TDBF):Boolean;far; Функция-фильтр должна быть глобальной в вашей программе или модуле. В качестве параметра она получает объект, коий ей фильтруется и может пользоваться методами GetXXXfield для доступа к его полям. Она должна возвращать True если текущая запись удовлетворяет условию и False - если нет. Установка фильтра производится с помощью метода procedure SetFilterTo(AFilter:TFilter); где тип TFilter описан как function(var D:TDBF):Boolean; В качестве фактического параметра передается идентификатор функции. Такой метод передачи параметра позволяет на этапе компиляции проверить соответствие функции требуемому виду. При несоответсвтии типа функции вы получите сообщение Type mismatch, а если функция локальная или не объявлена как Far, то Invalid procedure or function reference. В случае, если фильтр не был установлен, или был сброшен процедурой RemoveFilter, то все записи доступны. Объект TIndexedDBF Отличается от TFilteredDBF тем, что записи в базе данных упорядочены с помощью индексного файла. Индексных файлов может быть несколько, при этом упорядочение задается только одним (последним из заданных) а при изменении базы обновляются все. деструктор этого объекта считает, что все индексы размещаются в динамической памяти и вызывает для них Dispose. Установка индекса производится методом procedure SetIndexTo(P:PIndex); Сделать уже открытый индекс текущим можно методом procedure SetOrderTo(n:Integer); (индексом номер 1 считается текущий, 2 - предыдущий и т.д.) Индекс - объект - наследник абстрактного объекта TIndex Объект TIndex Это абстрактный объект, реализующий метод работы с индексами, требующийся TIndexedDBF. Он имеет поле Typ типа IndexType которое может иметь два значение itpString и itpNumeric,поле Next - указатель на следующий индекс в списке и методы: Function First:LongInt;virtual; Возвращает номер первой по порядку индекса. Используется в TIndexedDBF.GO(Top) Function NextKey:LongInt;virtual; Возвращает следующий номер записи после текущего или -1 если текущее значение последнее. Function PrevKey:LongInt;virtual; возвращает номер предыдущей записи или -1, если запись первая Далее идут две функции поиска. Совместить поиск по строковому и числовому ключу, увы не удалось. Поэтому для любого из индексов одна из этих функций будет производить поиск, а вторая заносить в dbError значение dbInvalidType. Обе функции должны возвращать номер записи, ближайшей к указанному ключу и помещать в поле Exact значение True, если найдена запись с точно совпадающим ключом и False, если такой записи нет. Это единственные методы объекта TIndex, которые вам надо вызывать непосредственно. Все остальные методы представляют собой интерфейс с объектом TDBF Function SeekStr(Key:String):LongInt;virtual; Поиск - в случае строкового индекса} Function SeekNum(Key:Float):LongInt;virtual; Поиск - в случае числового индекса} Procedure UpdateKey;virtual; эта процедура вызывается при уходе из записи. Она должна обновить значение ключа, если ключевое поле изменилось, вставить новую запись в индекс, если добавлена новая запись и удалять , если запись удалена. Объект TMemoFile Это тоже абстрактный объект, реализующий работу с файлом типа Memo. Он не имеет полей, но имеет следующие методы function GetMemoSize(Offset:LongInt):word;virtual; Возвращает размер текста указанного поля Memo для поиска нужного поля в параметре Offset передается содержимое поля в файле dBF; Procedure Get(Offset:LongInt;var P);virtual; Записывает в блок памяти, на который указывает указатель P содержимое указаного поля Memo. Формат записи соответствует структре TMemoRec из модуля Editors Turbo Vision - сначала слово длины, а потом сам текст. Function Put(var P):LongInt;virtual; -записывает в файл текст Memo. Возвращает число, которое следует поместить в соответствующее поле DBF-файла. Procedure Free(Offset:LongInt);virtual; - освобождает участок в файле Memo, добавляя его в список свободных. Function GetDBFID:Byte;virtual; возвращает байт типа базы данных, который нужно поместить в заголовок файла DBF. Этот байт должен иметь значение $83 для Memo в формате Dbase III + или FoxBase+, (расширение Memo-файла DBT) $F5 для Memo в формате FoxPro (FPT) и $8B для Memo в формате dBase IV СООБЩЕНИЯ ОБ ОШИБКАХ В модуле dBase определена глобальная переменная dbError, куда практически каждая операция помещает код завершения. Этот код может быть 0 - ошибок нет, отрицательный - что-то не так, но операция выполнена и положительный - операцию выполнить не удалось. Такое кодирование облегчает при необходимости игнорирование несущественных ошибок. Для облегчения работы с кодами, каждому из них соответствует символическая константа с длинным, но удобочитаемым названием. Итак, ошибки бывают: dbFieldEmpty=-1; Метод GetXXXField обнаружил пустое поле, возвращено значение по умолчанию (0 - для числовых поле, False - для логических. Для строки это вообще нормальная ситуация- пустая строка). dbDataTooLong=-2; Попытка PutField строку, превышающую длину поля. Она обрезана по границе поля. dbRecordNotDeleted=-3; Попытка Recall запись, не помеченную для удаления. Ничего не сделано, но результат тот, которого вы добивались. dbAlreadyDeleted=-4; Наоборот, попытка повторно удалить запись. Все, что далее - более серьезно: dbInvalidMode=1; Попытка добавить поле в существующую базу данных или, наоборот, писать данные (или читать) в создаваемую. Попытка изменить файл, открытый только для чтения. dbInvalidField=2; Попытка обратиться к несуществующему полю или найти в базе данных поле с длиной имени более 11 символов. dbInvalidRecNo=3; Попытка обратиться к записи с очень большим или отрицательным номером dbInvalidFieldName=4; Попытка использовать русские буквы в имени поля dbInvalidType=5; Неправильный тип поля (PutRealField в строковое поле или что-нибудь в этом роде dbInvalidData=6; Данные не соответствуют типу поля. Например буква в числовом поле. dbLimitExeeded=7; Данные не умещаются в числовое поле. Ничего не записывается. dbBaseEmpty=8; Попытка читать из пустой базы данных dbRecordTooLarge=9; Попытка добавить в базу данных поле, вместе с которым длина записи превысит 4 К dbFieldAlreadyExist=10; Попытка добавить поле имя которого совпадает с уже существующим. dbInvalidIndexNum=11; В SetOrderTo передано отрицательное число или число, превышающее количество открытых индексов. dbInvalidIndex=12; Зарезервировано для использования наследниками от TIndex dbUndefinedIndex =13; dbMemoNotOpen=14; Попытка обратиться к Memo полю, не инициализировав объект типа TMemoFile и/или не поместив ссылку на него в поле Memo объекта TDBF. Замечания по beta-версии 0.90 В версию 0.95 beta данной библиотеки включен модуль FOXPRO.PAS, в котором не реализованы функции обновления индекса. При попытке изменить ключевое поле или удалить запись программа будет слетать по ошибке abstract method call. Данную особенность ни в коем случае не следует расценивать как стремление автора содрать деньги за полную версию. Просто я еще не дописал эту вещь, но уже имел неосторожность разрекламировать ее в Relcom, после чего был завален просьбами ее прислать. Поэтому получайте пока AS IS. По моему мнению даже в таком урезаном виде модуль FOXPRO полезен, ибо позволяет работать с индексами FOXPRO в режиме ReadOnly, а это тоже иногда бывает нужно. Модуль FOXPRO, вернее то, что в нем работает, тестировался с индексными файлами, созданными FOXPRO ver 1.00 US/Canadion Edition и Foxpro 2.5B for DOS Сведений о том, чьи зарегестрированные тороговые марки использованы в тексте, не привожу, бо лень. С вопросами, предложениями и дополнениями обращаться по адресу (relcom) vitus@agropc.msk.su тел. (095) 230-80-61 (р ~ с 9:00 до 17:00) ФИДО нет