Unit: dBase
-
- Registriert: 04. Dez 2006
- Letzter Besuch: 09. Sep 2015
- Beiträge: 96
Hello
I have write a unit to read and write a DBF-File (Version III, III+, IV):
Code
Unit uDBase;
// Unit fuer DBase III
Interface
Uses uStream, uSysInfo,
uString, SysUtils;
Const MaxFields = 127; // Von 0 bis 127
type tDBaseFieldTyp = (tDBaseFT_Char,
tDBaseFT_Numeric,
tDBaseFT_Logical,
tDBaseFT_Date,
tDBaseFT_Memo);
tDBaseVersion = (tDBaseVer_III, // DBase-Version III(+)
tDBaseVer_IV); // DBase-Version IV
type tcDBaseHeaderField = class
private
fName : tStr11;
fTyp : tDBaseFieldTyp;
fLength : LongWord;
fDecimal: LongWord;
public
Constructor Create(Const iName : tStr11; Const iTyp : tDBaseFieldTyp;
Const iLength : LongWord; Const iDecimal : LongWord); virtual;
Property Name : tStr11 Read fName;
Property Typ : tDBaseFieldTyp Read fTyp;
Property Length : LongWord Read fLength;
Property Decimal: LongWord Read fDecimal;
end;
type tArrDBaseHeaderFields = Array[0..MaxFields] of tcDBaseHeaderField;
tArrDBaseHeaderFieldPos= Array[0..MaxFields] of LongWord;
type tcDBaseDBT = class
private
fVersion : tDBaseVersion; // Version der DBase-Datei
fFileName : tFileName; // Name der Memo-Datei
fFileStream : tFileStream; // Stream-Objekt
fBlockSize : LongWord;
fBlockBuffer: pByteArray;
fNextFreeRec: LongInt; // Naechster freie Block
Procedure ReadMemoryHeader;
Function ReadMemoryText(iBlockID : LongWord) : AnsiString;
public
Constructor Create(Const iFileName : tFileName; iVersion : tDBaseVersion); virtual;
Destructor Destroy; override;
Property FileName : tFileName Read fFileName;
Property BlockSize: LongWord Read fBlockSize;
end;
type tcDBase = class
private
fVersion : tDBaseVersion; // Version der DBase-Datei
fFileName : tFilename; // Name der Datenbank
fFileStream : tFileStream; // Stream-Objekt
fDBaseDBT : tcDBaseDBT; // Objekt fuer die DBT-Datei
fHeaderSize : LongWord; // Groesse der Headerinformation
fRecordCount : LongWord; // Anzahl der Saetze
fRecordSize : Word; // Laenge des Datensatzes
fFilePosFieldDef: LongWord; // Zeigt auf das 1. Feld in der Datei
fFilePosData : LongWord; // Zeigt auf den 1. Satz in der Datei
fFieldCount : LongWord; // Anzahl der Felder
fHeaderFieldMod : Boolean; // =true...Header darf noch veraendert werden
fHeaderFields : tArrDBaseHeaderFields;
fHeaderFieldsPos: tArrDBaseHeaderFieldPos;
fRow : LongWord;
fRecordBuffer : PByteArray;
fRecordDeleted : Boolean;
fMemoryNextFreeRecord: LongWord;
Procedure WriteHeader;
Procedure ReadHeader;
Procedure WriteFields;
Procedure ReadFields;
Procedure SetRow(iRow : LongWord);
Procedure SetRecordDeleted(iDeleted : Boolean);
Function getData(iFieldPos : LongWord) : Variant;
Procedure setData(iFieldPos : LongWord; iData : Variant);
Function CalcFilePosition : LongInt;
public
Procedure AddField(iName : tStr10; iTyp : tDBaseFieldTyp;
iLength, iDecimal : Byte);
Procedure AppendBlank;
Procedure Update;
Constructor Create(Const iFileName : tFileName); virtual;
Destructor Destroy; override;
Property FileName : tFileName Read fFileName;
Property Version : tDBaseVersion Read fVersion;
Property Row : LongWord Read fRow Write SetRow;
Property FieldCount : LongWord Read fFieldCount;
Property Fields : tArrDBaseHeaderFields Read fHeaderFields;
Property HeaderFieldMod: Boolean Read fHeaderFieldMod;
Property RecordCount : LongWord Read fRecordCount;
Property RecordSize : Word Read fRecordSize;
Property RecordDeleted : Boolean Read fRecordDeleted Write SetRecordDeleted;
Property Data[FieldPos: LongWord]: Variant Read getData Write SetData;
end;
Implementation
// Konstanten
Const DBaseVersionDBII = ; // DBase II-Datei
DBaseVersionDBIII = ; // DBase III/IV/FoxBase+/FoxPro 2x-Datei ohne Memofeld
DBaseVersionDBIIIM = ; // DBase III-Datei mit Memofeld
DBaseVersionDBIVM = B; // DBase IV-Datei mit MemoFeld
DBaseVersionFoxPro2 = $F5; // FoxPro-Version 2.x mit Memo
DBaseVersionFoxPro3 = ; // Visual-FoxPor 3.0
fmOpen = fmOpenReadWrite Or fmShareDenyWrite;
Const DBaseFieldType_Char = 'C';
DBaseFieldType_Numeric_1 = 'N';
DBaseFieldType_Numeric_2 = 'F';
DBaseFieldType_Logical = 'L';
DBaseFieldType_Date = 'D';
DBaseFieldType_Memo = 'M';
// Header-Definition
type tDBFHeader = Record
Version : Byte; // Version von der DBase-Datei
WriteDate : Record // Letztes Datum des Schreibens
Year : Byte; // Format: YY
Month : Byte; // Format: MM
Day : Byte; // Format: DD
End;
RecCount : LongWord; // Zahl der Datensaetze
HeaderSize : Word; // Anzahl der Bytes von dem Header + Felddefinition
RecordSize : Word; // Datensatzlaenge
Reserved1 : Word; // Reserviert
FlagNotFinishTrans: Byte; // Flag fuer nichtbeendete Transaktionen
FlagScrambling : Byte; // Flag fuer die Verschluesselung
Reserved2 : Array[0..11] of Byte;
ExistsMDX : Byte; // Bei 01H existiert eine MDX-Datei
Reserved3 : Array[0..2] of Byte;
End;
type tDBFField = Record
FieldName : tChr10; // Name der Spalte
FieldType : Char; // Typ des Feldes
FieldAddress: LongWord; // Datenadresse des Feldes im Speicher
FieldLen : Byte; // Laenge des Feldes
FieldDec : Byte; // Zahl der Nachkommastellen
Reserved : Array[1..14] of Byte;
End;
type tDBTHeader = Record
NextFreeRecord : LongWord; // Zeiger auf den ersten freien Block
Reserved1 : LongWord; // wird nicht benuetzt
FileNameDBF : tChr8; // Name der DBF-Datei
DBIIIHeader : Byte; // Sagt aus ob es sich um eine DBIII oder DBIV handelt
Reserved2 : Array[0..2] of Byte;
BlockSize
: Array[0..1] of Byte; // Laenge des Blocks
End;
type tDBTIVBlockHeader = Record
BlockStart : LongWord;
TextLen : LongWord;
End;
// ---------------------------------------------------------------------------------
Function tcDBase.getData(iFieldPos : LongWord) : Variant;
// Lesen des angebenen Feldes.
var Field : tcDBaseHeaderField;
Data : String;
Len : LongWord;
p : LongWord;
l : LongInt;
dt : tDateTime;
Begin
Field:=fHeaderFields[iFieldPos];
len:=Fields[iFieldPos].Length;
p:=fHeaderFieldsPos[iFieldPos];
Data[0]:=chr(len);
move(fRecordBuffer[p], Data[1], Len);
Case Field.Typ of
tDBaseFT_Char : Result:=TrimRight(Data);
tDBaseFT_Numeric: if Fields[iFieldPos].Decimal=0 then
Begin
l:=StrToInt(Trim(Data));
Result:=l;
End
else
Begin
Writeln(Data);
// StrToFloat
End;
tDBaseFT_Logical: Result:=(Data[1] in ['T','t','J','j']);
tDBaseFT_Date : Begin
Data:=trim(Data);
if Data='' then
dt:=0
else
dt:=EncodeDate(
StrToInt(Copy(Data,1,4)),
StrToInt(Copy(Data,5,2)),
StrToInt(Copy(Data,7,2)));
Result:=dt;
End;
tDBaseFT_Memo : Begin
Data:=trim(Data);
if Data<>'' then
Begin
l:=StrToInt(Data);
if l>0 then
Result:=fDBaseDBT.ReadMemoryText(l);
End;
End;
End;
End;
Procedure tcDBase.setData(iFieldPos : LongWord; iData : Variant);
// Das angegebene Feld mit den Daten befuellen.
var Field : tcDBaseHeaderField;
Data : String;
Len : LongWord;
p : LongWord;
DataTyp: Word;
Function Date2dBase : String;
var y,m,d : Word;
sy : tStr4;
sm,sd : tStr2;
Begin
DecodeDate(iData,y,m,d);
sy:=IntToStr(y);
sm:=IntToStr(m);
sd:=IntToStr(d);
sy:=iif(y<10,' '+sy,sy);
sm:=iif(m<10,' '+sm,sm);
sd:=iif(d<10,' '+sd,sd);
Result:=sy+sm+sd;
End;
label exit_error;
Begin
Field:=fHeaderFields[iFieldPos];
len:=Fields[iFieldPos].Length;
p:=fHeaderFieldsPos[iFieldPos];
DataTyp:=VarType(iData);
Case Field.Typ of
tDBaseFT_Char : Begin
if DataTyp<>VarString then goto exit_error;
Data:=iData;
Data:=Data+FillString(Len-Length(Data), #32);
End;
tDBaseFT_Numeric: Begin
if not DataTyp in [VarByte, VarWord, VarLongWord]
then goto exit_error;
Data:=inttostr(iData);
Data:=FillString(Len-Length(Data), #32)+Data;
End;
tDBaseFT_Logical: Begin
if DataTyp<>VarBoolean then goto exit_error;
Data:=iif(iData,'T','F');
End;
tDBaseFT_Date : Begin
if DataTyp<>VarExtended then goto exit_error;
Data:=Date2dBase;
End;
tDBaseFT_Memo : Begin
End;
End;
move(Data[1], fRecordBuffer[p], Len);
exit;
exit_error:
Writeln('falsches Format');
Readln;
End;
Function tcDBase.CalcFilePosition : LongInt;
Begin
Result:=fFilePosData+(fRow * (fRecordSize+1))
End;
Procedure tcDBase.SetRow(iRow : LongWord);
// An den angegeben Record springen
var chdel : Char;
Begin
if iRow>=fRecordCount then exit;
fRow:=iRow;
fFileStream.Position:=CalcFilePosition;
fFileStream.ReadBuffer(chdel,1);
fRecordDeleted:=chdel='*';
fFileStream.ReadBuffer(fRecordBuffer^,fRecordSize);
End;
Procedure tcDBase.SetRecordDeleted(iDeleted : Boolean);
// Setzen des Record-Delete-Zeichens
var chdel : Char;
Begin
fRecordDeleted:=iDeleted;
if fRecordDeleted
then chDel:='*'
else chDel:=' ';
fFileStream.Position:=CalcFilePosition;
fFileStream.WriteBuffer(chdel,1);
End;
Procedure tcDBase.AddField(iName : tStr10; iTyp : tDBaseFieldTyp;
iLength, iDecimal : Byte);
// Ein neues Feld hinzufuegen. 4
var p : LongWord;
Begin
if fHeaderFieldMod=false then exit;
// Writeln(fFieldCount,':',iName,',',ord(iTyp),',',iLength,',',iDecimal);
fHeaderFields[fFieldCount].Create(iName, iTyp, iLength, iDecimal);
if fFieldCount=0
then p:=0
else p:=fHeaderFieldsPos[fFieldCount-1] + fHeaderFields[fFieldCount-1].Length;
fHeaderFieldsPos[fFieldCount]:=p;
inc(fFieldCount);
End;
Procedure tcDBase.Update;
// Aktuellen Satz Updaten.
Begin
// Positionieren im Stream. +1 da das 1. Byte das Delete-Flag ist.
fFileStream.Position:=CalcFilePosition+1;
// Writeln('Upd pos:',fFileStream.Position,',',fRecordSize);
fFileStream.WriteBuffer(fRecordBuffer^,fRecordSize);
End;
Procedure tcDBase.AppendBlank;
// Einen leeren Satz hinzufuegen.
Var chdel : Char;
Begin
// Wenn der 1. Satz geschrieben wird, dann die Feld-Definitionen auch schreiben
if fHeaderFieldMod then
Begin
WriteHeader;
WriteFields;
fHeaderFieldMod:=false;
End;
// Anzahl der Saetze und die aktuelle Position berechnen
inc(fRecordCount);
fRow:=fRecordCount-1;
fFileStream.Position:=CalcFilePosition;
// Loesch-Kennzeichen speichern
chdel:=' ';
fFileStream.WriteBuffer(chdel,1);
// Record speichern
// Writeln('Apd pos:',fFileStream.Position);
FillChar(fRecordBuffer^, fRecordSize, #32);
fFileStream.WriteBuffer(fRecordBuffer^,fRecordSize);
// Header aktualisieren
WriteHeader;
SetRow(fRow);
End;
Procedure tcDBase.WriteHeader;
// Schreiben des Headers
var Header : tDBFHeader;
Y, M, D: Word;
Begin
fFileStream.Position:=0;
FillChar(Header, Sizeof(tDBFHeader), #0);
Header.Version :=DBaseVersionDBIII;
DecodeDate(now,Y,M,D);
if Y<2000
then Y:=Y-1900
else Y:=Y-2000;
Header.WriteDate.Year :=Y;
Header.WriteDate.Month:=M;
Header.WriteDate.Day :=D;
Header.RecCount := fRecordCount;
Header.HeaderSize := fHeaderSize;
Header.RecordSize := fRecordSize+1;
fFileStream.WriteBuffer(Header, Sizeof(tDBFHeader));
fFilePosFieldDef:=fFileStream.Position;
End;
Procedure tcDBase.ReadHeader;
// Lesen der Header-Informationen
var Header : tDBFHeader;
FileNameDBT: tFileName;
Begin
fFileStream.Position:=0;
FileNameDBT:='';
fFileStream.ReadBuffer(Header, Sizeof(tDBFHeader));
fRecordSize:=Header.RecordSize-1;
case Header.Version of
DBaseVersionDBIII: Begin
fVersion:=tDBaseVer_III;
End;
DBaseVersionDBIIIM: Begin
fVersion:=tDBaseVer_III;
FileNameDBT:=ChangeFileExt(fFileName,EXT_UC_DBT);
End;
// DBaseVersionDBIV: fVersion:=tDBaseVer_IV;
DBaseVersionDBIVM : Begin
fVersion:=tDBaseVer_IV;
FileNameDBT:=ChangeFileExt(fFileName,EXT_UC_DBT);
End;
else Begin
Writeln('Falsche DBase-Version');
Exit;
End;
end;
fHeaderSize :=Header.HeaderSize;
fRecordCount :=Header.RecCount;
fFilePosFieldDef:=fFileStream.Position;
GetMem(fRecordBuffer, fRecordSize);
// Memory-Datei verarbeiten
if FileExists(FileNameDBT)
then fDBaseDBT.Create(FileNameDBT, fVersion)
else fDBaseDBT:=nil;
End;
Procedure tcDBase.WriteFields;
var FNr : LongWord;
MemField: tcDBaseHeaderField;
Field : tDBFField;
NameC : cString[10];
efd : byte; // Ende der Felddefinition
Begin
fFileStream.Position:=fFilePosFieldDef;
fRecordSize:=0;
for FNr:=0 to fFieldCount-1 do
Begin
fillchar(Field,sizeof(tDBFField),#0);
MemField:=fHeaderFields[FNr];
NameC:=MemField.Name;
StrLCopy(Field.FieldName, @NameC, length(MemField.Name));
Case MemField.Typ of
tDBaseFT_Char : Field.FieldType:=DBaseFieldType_Char;
tDBaseFT_Numeric: Field.FieldType:=DBaseFieldType_Numeric_1;
tDBaseFT_Logical: Field.FieldType:=DBaseFieldType_Logical;
tDBaseFT_Date : Field.FieldType:=DBaseFieldType_Date;
tDBaseFT_Memo : Field.FieldType:=DBaseFieldType_Memo;
end;
Field.FieldLen:=MemField.Length;
Field.FieldDec:=MemField.Decimal;
fFileStream.WriteBuffer(Field, SizeOf(tDBFField));
fRecordSize:=fRecordSize+Field.FieldLen;
End;
// Ende Zeichen schreiben
efd:= PNFSTREPLACEMENT0 d;
fFileStream.WriteBuffer(efd, 1);
// Headerinformationen speichern
fHeaderSize:=fFileStream.Position;
fFilePosData:=fHeaderSize;
// RecordBuffer definieren
if fRecordBuffer=nil then
GetMem(fRecordBuffer, fRecordSize-1);
End;
Procedure tcDBase.ReadFields;
// Lesen der Feld-Informationen
var Field : tDBFField;
NameC : tChr20;
Typ : tDBaseFieldTyp;
si : LongWord;
efd : byte; // Ende der Felddefinition
Begin
fFileStream.Position:=fFilePosFieldDef;
si:=fHeaderSize-1;
Repeat
fFileStream.ReadBuffer(Field, SizeOf(tDBFField));
FillChar(NameC, sizeof(NameC), #0);
strLCopy(NameC, Field.FieldName, 11);
Case Field.FieldType of
DBaseFieldType_Char : Typ:=tDBaseFT_Char;
DBaseFieldType_Numeric_1,
DBaseFieldType_Numeric_2 : Typ:=tDBaseFT_Numeric;
DBaseFieldType_Logical : Typ:=tDBaseFT_Logical;
DBaseFieldType_Date : Typ:=tDBaseFT_Date;
DBaseFieldType_Memo : Typ:=tDBaseFT_Memo;
end;
AddField(strPas(NameC), Typ, Field.FieldLen, Field.FieldDec);
Until fFileStream.Position=si;
// Check auf Ende der FeldDefintion. Muss x0D
fFileStream.ReadBuffer(efd, 1);
if efd<> PNFSTREPLACEMENT0 d then
Writeln('falsches Feld-EndDefinition');
fFilePosData:=fFileStream.Position;
End;
Constructor tcDBase.Create(Const iFileName : tFileName);
// DBF-Datei oeffnen
Begin
inherited Create;
fFileName:=iFileName;
if ExtractFileExt(fFileName)='' then
fFileName:=fFileName+EXT_LC_DBF;
fHeaderFieldMod:=true;
fillChar(fHeaderFields, sizeof(tArrDBaseHeaderFields), #0);
if (FileExists(fFileName)) and (FileGetSize(fFileName)>0)
then // Datei existiert und wird geoeffnet
Begin
fFileStream.Create(fFileName,fmOpen);
ReadHeader;
if fRecordCount>0 then
Begin
ReadFields;
fHeaderFieldMod:=false;
End;
End
else // Datei neu erstellen
Begin
fFileStream.Create(fFileName,fmCreate); // Leere Datei anlegen
fRecordCount:=0;
fRecordSize :=0;
WriteHeader;
End;
End;
Destructor tcDBase.Destroy;
// DBF-Datei schliessen
var cou : LongInt;
Begin
if fRecordBuffer<>nil then
FreeMem(fRecordBuffer, fRecordSize);
for cou:=0 to fFieldCount-1 do
Begin
fHeaderFields[cou].Destroy;
End;
fFileStream.Destroy;
if fDBaseDBT<>nil then
fDBaseDBT.Destroy;
inherited Destroy;
End;
// ---------------------------------------------------------------------------------
Constructor tcDBaseHeaderField.Create(Const iName : tStr11; Const iTyp : tDBaseFieldTyp;
Const iLength : LongWord; Const iDecimal : LongWord);
// Speichern der Feld-Informationen im Speicher
Begin
inherited Create;
fName :=UpperCase(iName);
fTyp :=iTyp;
fDecimal:=0;
case fTyp of
tDBaseFT_Char : fLength :=iLength;
tDBaseFT_Numeric: Begin
fLength :=iLength;
fDecimal:=iDecimal;
End;
tDBaseFT_Logical: fLength:=1;
tDBaseFT_Date : fLength:=8;
tDBaseFT_Memo : fLength:=10;
end;
End;
// ---------------------------------------------------------------------------------
Procedure tcDBaseDBT.ReadMemoryHeader;
// Lesen der Memory-Header informationen
Var Header : tDBTHeader;
Begin
fFileStream.Position:=0;
fFileStream.ReadBuffer(Header, sizeof(tDBTHeader));
if fVersion=tDBaseVer_III
then fBlockSize:=512
else fBlockSize:=Header.BlockSize[0]*256+Header.BlockSize[1];
GetMem(fBlockBuffer,fBlockSize);
fNextFreeRec:=Header.NextFreeRecord;
End;
Function tcDBaseDBT.ReadMemoryText(iBlockID : LongWord) : AnsiString;
// Lesen des Memorys-Textes zum angegebenen Blocks
Procedure ReadDBIII;
var rb : LongInt;
cou: LongInt;
ex : Boolean;
Begin
ex:=false;
repeat
FillChar(fBlockBuffer^,fBlockSize,#0);
rb:=fFileStream.Read(fBlockBuffer^, fBlockSize);
for cou:=0 to rb-1 do
Begin
if fBlockBuffer^[cou]=A then
Begin
ex:=true;
continue;
End;
Result:=Result+chr(fBlockBuffer^[cou]);
End;
until ex;
End;
Procedure ReadDBIV;
var BlockHeader: tDBTIVBlockHeader;
p : pChar;
Begin
fFileStream.ReadBuffer(BlockHeader, sizeof(tDBTIVBlockHeader));
if BlockHeader.BlockStart<> PNFSTREPLACEMENT0 08FFFF then // In der Datei: FF FF 08 00
exit;
GetMem(p,BlockHeader.TextLen);
fFileStream.ReadBuffer(p^,BlockHeader.TextLen);
AnsiSetString(Result, p, BlockHeader.TextLen);
FreeMem(p,BlockHeader.TextLen);
End;
Begin
Result:='';
fFileStream.Position:=iBlockID * fBlockSize;
// Writeln(tohex(fFileStream.Position));
case fVersion of
tDBaseVer_III: ReadDBIII;
tDBaseVer_IV : ReadDBIV;
End;
End;
Constructor tcDBaseDBT.Create(Const iFileName : tFileName; iVersion : tDBaseVersion);
// Memory-Datei oeffnen.
Begin
inherited Create;
fFileName:=iFileName;
fVersion :=iVersion;
fFileStream.Create(fFileName,fmOpen);
ReadMemoryHeader;
End;
Destructor tcDBaseDBT.Destroy;
// Memory-Datei schliessen.
Begin
if fBlockBuffer<>nil then
FreeMem(fBlockBuffer,fBlockSize);
fFileStream.Destroy;
inherited Destroy;
End;
Initialization
End.
bye,
Wolfgang
- Moderiert von:
- Admins-Forum
Benutzer online
- 0 Benutzer
Diese Angaben basieren auf den Useraktivitäten der letzten 30 Minuten.