kolmck/Addons/KOLmdvDBF.pas

1293 lines
45 KiB
ObjectPascal

unit KOLmdvDBF;
// Êîìïîíåíò mdvDBF - ïðÿìîé äîñòóï ê DBF-ôàéëàì (ñ memo) áåç èñïîëüçîâàíèÿ äîïîëíèòåëüíîãî ÏÎ.
// E-Mail: dominiko-m@yandex.ru
// http://www.mdvkol.narod.ru/
// Àâòîð: Ìàòâååâ Äìèòðèé
// - Èñòîðèÿ -
// Äàòà: 01.03.2007 Âåðñèÿ: 1.03
{
[+] - äîáàâèë ìåòîä NewDbf
[*] - Ïîïðàâèë ñèãíàòóðó ìåìî-ïîëåé äëÿ dBaseIV
}
// Äàòà: 26.09.2005 Âåðñèÿ: 1.02
{
[+] - äîáàâèë íåñêîëüêî âñïîìîãàòåëüíûõ ìåòîäîâ
}
// Äàòà: 07.12.2004 Âåðñèÿ: 1.01
{
[+] - äîáàâèë ñèãíàòóðó ìåìî-ïîëåé
}
// Äàòà: 02.12.2004 Âåðñèÿ: 1.00
{Ñòàðòîâàÿ âåðñèÿ}
interface
uses Windows, KOL;
const
DBF_FoxBASE = $02;
DBF_FoxBASE_ = $FB;
DBF_dBaseIIIplus = $03;
DBF_dBaseIIIplusMemo = $83;
DBF_dBaseIV = $04;
DBF_dBaseIVSQLtable = $43;
DBF_dBaseIVSQLsystem = $63;
DBF_dBaseIVSQLtableMemo = $CB;
DBF_dBaseIVMemo = $8B;
DBF_dBaseV = $05;
DBF_FoxPro2xMemo = $F5;
DBF_VisualFoxPro = $30;
DBF_VisualFoxProInc = $31;
MsDos_US_437 = $01;
MsDos_Mazovia_Polish_620 = $69;
MsDos_Greek_737 = $6A;
MsDos_International_850 = $02;
MsDos_Eastern_European_852 = $64;
MsDos_Turkish_857 = $6B;
MsDos_Icelandic_861 = $67;
MsDos_Nordic_865 = $66;
MsDos_Russian_866 = $65;
MsDos_Kamenicky_Czech_895 = $68;
Windows_Thai_874 = $7C;
Windows_Japanese_932 = $7B;
Windows_Chinese_PRC_Singapore_936 = $7A;
Windows_Korean_949 = $79;
Windows_Chinese_HongKongSAR_Taiwan_950 = $78;
Windows_Eastern_European_1250 = $C8;
Windows_Russian_1251 = $C9;
Windows_ANSI_1252 = $03;
Windows_Greek_1253 = $CB;
Windows_Turkish_1254 = $CA;
Windows_Hebrew_1255 = $7D;
Windows_Arabic_1256 = $7E;
Windows_Standard_Macintosh_10000 = $04;
Windows_Greek_Macintosh_10006 = $98;
Windows_Russian_Macintosh_10007 = $96;
Windows_Macintosh_EE_10029 = $97;
type
TYYMMDD = array [1..3] of Byte;
TDBFHeader = packed record
DBFType: Byte; // Òèï ôàéëà (DBF_xxx)
LastUpdated: TYYMMDD; // Äàòà ïîñëåäíåãî îáíîâëåíèÿ â ôîðìàòå YYMMDD
RecordCount: DWord; // Êîëè÷åñòâî çàïèñåé â òàáëèöå
HeaderLength: Word; // Êîëè÷åñòâî áàéò, çàíèìàåìûõ çàãîëîâêîì
RecordLength: Word; // Êîëè÷åñòâî áàéò, çàíèìàåìûõ çàïèñüþ
Reserved_1: array [1..16] of Byte; // 3-Çàðåçåðâèðîâàííàÿ îáëàñòü; 13 - Çàðåçåðâèðîâàíî äëÿ ñåòåâîé âåðñèè dBASE III PLUS
TableFlags: Byte; // $01 - file has a structural .cdx; $02 - file has a Memo field; $04 - file is a database (.dbc). This byte can contain the sum of any of the above values. For example, the value 0x03 indicates the table has a structural .cdx and a Memo field.
CodePage: Byte; // Êîäîâàÿ ñòðàíèöà
Reserved_2: Word; // Çàðåçåðâèðîâàííàÿ îáëàñòü
end;
TFieldName = array [1..11] of Char;
TDBFField = packed record
FieldName: TFieldName; // Íàçâàíèå ïîëÿ
FieldType: Char; // Òèï ïîëÿ (C – Character; Y – Currency; N – Numeric; F – Float; D – Date; T – DateTime; B – Double; I – Integer; L – Logical; M – Memo; G – General; C – Character (binary); M – Memo (binary); P – Picture;
Address: DWord; // Àäðåñ ïîëÿ â çàïèñè
FieldLength: Byte; // Äëèíà ïîëÿ (â áàéòàõ)
Decimals: Byte; // Äëèíà äåñÿòè÷íîé ÷àñòè
FieldFlags: Byte; // Ôëàã ïîëÿ ($01 - System Column (not visible to user); $02 - Column can store null values; $04 - Binary column (for CHAR and MEMO only); $06($02+$04) - When a field is NULL and binary (Integer, Currency, and Character/Memo fields); $0C - Column is autoincrementing;
AutoIncNext: DWord; // Ñëåäóþùåå çíà÷åíèå äëÿ Àâòîèíêðèìåíòà
AutoIncStep: Byte; // Øàã Àâòîèíêðèìåíòà
Reserved3 : array[1..7] of Byte; // Çàðåçåðâèðîâàííàÿ îáëàñòü
IndexFlag : Byte; // Ôëàã MDX-ïîëÿ: $01 åñëè ïîëå èìååò ìåòêó èíäåêñà â MDX-ôàéëå, $00 - íåò.
end;
TFPTHeader = packed record
NextFree: DWord; // * Location of next free block
Unused: Word; // Unused
BlockSize: Word; // * Block size (bytes per block)
Unused_2: array [0..503] of Byte;
end;
TDBTHeader = packed record
NextFree: DWord; // Location of next free block
Reserved1: DWord;
DbfFileName: array [1..9] of Char; // Name parent DBF table
reserved2: array [1..3] of Char;
BlockSize: Word;
end;
TMemoBlockHeader = packed record
BlockSignature: DWord; // * Block signature (indicates the type of data in the block) (0 – picture (picture field type); 1 – text (memo field type))
Length: DWord; // * Length of memo (in bytes)
//08–n Memo text (n = length)
end;
PRecordBuffer = ^TRecordBuffer;
TRecordBuffer = array[0..0] of Byte;
PDBFFields = ^TDBFFields;
TDBFFields = array [0..0] of TDBFField;
TFieldType = (ftUnknown, ftCharacter, ftCurrency, ftNumeric,
ftFloat, ftDate, ftDateTime, ftBinary,
ftInteger, ftLogical, ftMemo, ftGeneral,
ftCharacterBin, ftMemoBin, ftPicture);
TDbfErrors = (eNoErrors, eUnknownError, eFileNotExist, eFileOpen, eDBFHeader, eAppendRecord, eReadRecord, eWriteRecord, eReadField, eWriteField, eInvalidValue);
TOnDbfEvent = procedure(Sender: PObj; var Allowed: Boolean) of object;
TMemoType = (mtUnknown, mtFoxPro, mtdBaseIV, mtdBase);
PmdvDBF = ^TmdvDBF;
TKOLmdvDBF = PmdvDBF;
TmdvDBF = object(TObj)
private
FDBFHeader: TDBFHeader;
FDBFFields: PDBFFields;
FRecordBuffer: PRecordBuffer;
FFieldsCount: Integer;
FFPTHeader: TFPTHeader;
FDBTHeader: TDBTHeader;
FHasMemo: Boolean;
FActive, FAutoUpdate, FReadOnly, FDBFModified, FRecordModified: Boolean;
FFileName: String;
FFileNameMemo: String;
FDBFStream, FDBFMemoStream: PStream;
FCurrentRecord: DWord;
FBOF, FEOF: Boolean;
FError: TDbfErrors;
FOnScroll, FOnDelete, FOnAppend: TOnDbfEvent;
function InvertDWord(var Value: DWord): DWord;
function ReadFields: Boolean;
procedure ReadRecord;
procedure WriteRecord;
procedure SetDate(var ADate: TYYMMDD);
function GetLastUpdated: TDateTime;
function GetHasMemo: Boolean;
function GetMemoInfo(Index: Integer; var ABlockNum, ABlockSize, ABlockCount, ASize: DWord): TMemoType;
procedure SetActive(const Value: Boolean);
procedure SetFileName(const Value: String);
function GetError: TDbfErrors;
function GetFieldName(Index: Integer): String;
function GetFieldNumber(NameField: String): Integer;
function GetFieldType(Index: Integer): TFieldType;
function GetFieldDecimals(Index: Integer): Byte;
function GetFieldLength(Index: Integer): Byte;
procedure SetCurrentRecord(const Value: DWord);
function GetIsDelete: Boolean;
procedure SetIsDelete(const Value: Boolean);
function GetFieldIsString(Index: Integer): Boolean;
function GetFieldIsBoolean(Index: Integer): Boolean;
function GetFieldIsDateTime(Index: Integer): Boolean;
function GetFieldIsFloat(Index: Integer): Boolean;
function GetFieldIsInteger(Index: Integer): Boolean;
function GetAsText(Index: Integer): String;
function GetAsString(Index: Integer): String;
function GetAsBoolean(Index: Integer): Boolean;
function GetAsDateTime(Index: Integer): TDateTime;
function GetAsFloat(Index: Integer): Double;
function GetAsInteger(Index: Integer): Integer;
procedure SetString(Index: Integer; Value: String);
procedure SetAsText(Index: Integer; Value: String);
procedure SetAsString(Index: Integer; const Value: String);
procedure SetAsBoolean(Index: Integer; const Value: Boolean);
procedure SetAsDateTime(Index: Integer; const Value: TDateTime);
procedure SetAsFloat(Index: Integer; const Value: Double);
procedure SetAsInteger(Index: Integer; const Value: Integer);
function GetFieldIsNull(Index: Integer): Boolean;
function GetFieldIsMemo(Index: Integer): Boolean;
function GetMemoAsString(Index: Integer): String;
procedure SetMemoAsString(Index: Integer; Value: String);
function GetAsBooleanByName(AFieldName: String): Boolean;
function GetAsDateTimeByName(AFieldName: String): TDateTime;
function GetAsFloatByName(AFieldName: String): Double;
function GetAsIntegerByName(AFieldName: String): Integer;
function GetAsStringByName(AFieldName: String): String;
function GetAsTextByName(AFieldName: String): String;
procedure SetAsBooleanByName(AFieldName: String; const Value: Boolean);
procedure SetAsDateTimeByName(AFieldName: String; const Value: TDateTime);
procedure SetAsFloatByName(AFieldName: String; const Value: Double);
procedure SetAsIntegerByName(AFieldName: String; const Value: Integer);
procedure SetAsStringByName(AFieldName: String; const Value: String);
procedure SetAsTextByName(AFieldName: String; const Value: String);
function GetMemoAsStringByName(AFieldName: String): String;
procedure SetMemoAsStringByName(AFieldName: String; const Value: String);
public
destructor Destroy; virtual;
property DBFType: Byte read FDBFHeader.DBFType; // Òèï ôàéëà (DBF_xxx)
property LastUpdated: TDateTime read GetLastUpdated; // Äàòà ïîñëåäíåãî îáíîâëåíèÿ
property RecordLength: Word read FDBFHeader.RecordLength; // Êîëè÷åñòâî áàéòîâ, çàíèìàåìûõ çàïèñüþ
property HasMemo: Boolean read GetHasMemo;
property CodePage: Byte read FDBFHeader.CodePage;
property FieldsCount: Integer read FFieldsCount;
property FieldName[Index: Integer]: String read GetFieldName;
property FieldNumber[NameField: String]: Integer read GetFieldNumber;
property FieldType[Index: Integer]: TFieldType read GetFieldType;
property FieldLength[Index: Integer]: Byte read GetFieldLength;
property FieldDecimals[Index: Integer]: Byte read GetFieldDecimals;
property RecordCount: DWord read FDBFHeader.RecordCount; // Êîëè÷åñòâî çàïèñåé â òàáëèöå
property CurrentRecord: DWord read FCurrentRecord write SetCurrentRecord;
property BOF: Boolean read FBOF;
property EOF: Boolean read FEOF;
procedure First;
procedure Last;
procedure Next;
procedure Prev;
function Locate(AFieldName, AValue: String): Boolean; overload;
function Locate(AFieldNames, AValues: array of String): Boolean; overload;
function MaxOfField(AFieldName: String): Integer;
procedure Append;
procedure Post;
property IsDelete: Boolean read GetIsDelete write SetIsDelete;
procedure RefreshRecord;
procedure PackDBF;
{ DescriptionDBF format:
<CodePage>#2<BlockSize>#1
<FieldName1>#2<FieldType1>#2<FieldLength1>#2<Decimals1>#1
<FieldName2>#2<FieldType2>#2<FieldLength2>#2<Decimals2>#1
...
<FieldNameN>#2<FieldTypeN>#2<FieldLengthN>#2<DecimalsN>#1
BlockSize: recommend values 64, 128, 256, 512, 1024, 2048;
FieldName: max length: 10
FieldType: 'C' - Character; 'N' - Numeric; 'D' - Date; 'B' - Binary; 'L' - Logical, 'M' - Memo
FieldLength: 'C' - 1..254; 'N' - 1..20; 'D' - 8; 'L' - 1; 'M' - 10; 'B' - 10
Decimals: 'C' - 0; 'N' - 0..FieldLength-1; 'D' - 0; 'L' - 0; 'M' - 0; 'B' - 0
}
function NewDbf(AFileName: String; DescriptionDBF: String): Boolean;
property FieldIsString[Index: Integer]: Boolean read GetFieldIsString;
property FieldIsInteger[Index: Integer]: Boolean read GetFieldIsInteger;
property FieldIsFloat[Index: Integer]: Boolean read GetFieldIsFloat;
property FieldIsDateTime[Index: Integer]: Boolean read GetFieldIsDateTime;
property FieldIsBoolean[Index: Integer]: Boolean read GetFieldIsBoolean;
property FieldIsNull[Index: Integer]: Boolean read GetFieldIsNull;
property AsText[Index: Integer]: String read GetAsText write SetAsText;
property AsString[Index: Integer]: String read GetAsString write SetAsString;
property AsInteger[Index: Integer]: Integer read GetAsInteger write SetAsInteger;
property AsFloat[Index: Integer]: Double read GetAsFloat write SetAsFloat;
property AsDateTime[Index: Integer]: TDateTime read GetAsDateTime write SetAsDateTime;
property AsBoolean[Index: Integer]: Boolean read GetAsBoolean write SetAsBoolean;
property AsTextByName[FieldName: String]: String read GetAsTextByName write SetAsTextByName;
property AsStringByName[FieldName: String]: String read GetAsStringByName write SetAsStringByName;
property AsIntegerByName[FieldName: String]: Integer read GetAsIntegerByName write SetAsIntegerByName;
property AsFloatByName[FieldName: String]: Double read GetAsFloatByName write SetAsFloatByName;
property AsDateTimeByName[FieldName: String]: TDateTime read GetAsDateTimeByName write SetAsDateTimeByName;
property AsBooleanByName[FieldName: String]: Boolean read GetAsBooleanByName write SetAsBooleanByName;
procedure GetValue(Index: Integer; var Value);
procedure SetValue(Index: Integer; var Value);
property FieldIsMemo[Index: Integer]: Boolean read GetFieldIsMemo;
property MemoAsString[Index: Integer]: String read GetMemoAsString write SetMemoAsString;
property MemoAsStringByName[FieldName: String]: String read GetMemoAsStringByName write SetMemoAsStringByName;
function GetMemoValue(Index: Integer; var Value: Pointer): DWord;
procedure SetMemoValue(Index: Integer; Value: Pointer; ACount: DWord; ASigna: DWord = 0);
{ ??????? ftUnknown, ftCharacterBin, ftMemoBin, ftCurrency, ftInteger, }
property Active: Boolean read FActive write SetActive;
property AutoUpdate: Boolean read FAutoUpdate write FAutoUpdate;
Property DBFModified: boolean read FDBFModified;
property FileName: String read FFileName write SetFileName;
property FileNameMemo: String read FFileNameMemo write FFileNameMemo;
property Error: TDbfErrors read GetError;
property OnScroll: TOnDbfEvent read FOnScroll write FOnScroll;
property OnDelete: TOnDbfEvent read FOnDelete write FOnDelete;
property OnAppend: TOnDbfEvent read FOnAppend write FOnAppend;
end;
function NewmdvDBF(AFileName: String; AutoUpdate: Boolean; ReadOnly: Boolean = False): TKOLmdvDBF;
implementation
function NewmdvDBF(AFileName: String; AutoUpdate: Boolean; ReadOnly: Boolean = False): TKOLmdvDBF;
begin
New(Result, Create);
Result.FAutoUpdate:= AutoUpdate;
Result.FFileName:= AFileName;
Result.FReadOnly:= ReadOnly;
end;
{ TmdvDBF }
procedure TmdvDBF.Append;
var Allowed: Boolean;
begin
if not FActive or FReadOnly then Exit;
if FAutoUpdate then Post;
Allowed:= True;
if Assigned(FOnAppend) then FOnAppend(@Self, Allowed);
if not Allowed then Exit;
FDBFHeader.RecordCount:= FDBFHeader.RecordCount + 1;
try
FDBFStream.Seek(0, spBegin); FDBFStream.Write(FDBFHeader, SizeOf(FDBFHeader));
FCurrentRecord:= FDBFHeader.RecordCount-1;
FillMemory(FRecordBuffer, FDBFHeader.RecordLength, $20);
FRecordModified:= True;
Post;
except
FError:= eAppendRecord;
FDBFHeader.RecordCount:= FDBFHeader.RecordCount-1;
end;
end;
destructor TmdvDBF.Destroy;
begin
Active:= False;
inherited;
end;
procedure TmdvDBF.First;
begin
SetCurrentRecord(0);
FBOF:= FCurrentRecord=0; FEOF:= False;
end;
function TmdvDBF.GetAsBoolean(Index: Integer): Boolean;
begin
Result:= False;
try
if not FieldIsString[Index] then Exit;
Result:= (AsText[Index][1] in ['T', 't', 'Y', 'y'])
except
FError:= eReadField;
end;
end;
function TmdvDBF.GetAsDateTime(Index: Integer): TDateTime;
var SS, S: String;
begin
Result:= 0;
try
if not FieldIsDateTime[Index] then Exit;
SetLength(S, 14);
FillMemory(PChar(S), 14, Byte('0'));
SS:= AsText[Index];
Move(SS[1], S[1], Length(SS));
Result:= Str2DateTimeFmt('yyyyMMddHHmmss', S);
except
FError:= eReadField;
end;
end;
function TmdvDBF.GetAsFloat(Index: Integer): Double;
begin
Result:= 0;
try
if not FieldIsFloat[Index] then Exit;
Result:= Str2Double(Trim(AsText[Index]));
except
FError:= eReadField;
end;
end;
function TmdvDBF.GetAsInteger(Index: Integer): Integer;
begin
Result:= 0;
try
if not FieldIsInteger[Index] then Exit;
Result:= Str2Int(Trim(AsText[Index]));
except
FError:= eReadField;
end;
end;
function TmdvDBF.GetAsString(Index: Integer): String;
var S: String;
D: TDateTime;
begin
Result:= '';
try
if not FieldIsString[Index] then Exit;
S:= AsText[Index];
case FieldType[Index] of
ftCharacter: Result:= TrimRight(S);
ftDate, ftDateTime: begin
D:= AsDateTime[Index];
Result:= Date2StrFmt('dd.MM.yyyy', D);
if FieldType[Index] = ftDateTime then
Result:= Result + Time2StrFmt(' HH:mm:ss', D);
end;
ftLogical: if S[1] in ['T', 't', 'Y', 'y', 'F', 'f', 'N', 'n'] then Result:= S[1] else Result:= 'F';
ftFloat, ftNumeric: begin
Result:= Double2Str(AsFloat[Index]);
end;
end;
except
FError:= eReadField;
end;
end;
function TmdvDBF.GetError: TDbfErrors;
begin
Result:= FError;
FError:= eNoErrors;
end;
function TmdvDBF.GetFieldDecimals(Index: Integer): Byte;
begin
Result:= FDBFFields[Index].Decimals;
end;
function TmdvDBF.GetFieldIsBoolean(Index: Integer): Boolean;
begin
Result:= FieldType[Index] = ftLogical;
end;
function TmdvDBF.GetFieldIsDateTime(Index: Integer): Boolean;
begin
Result:= FieldType[Index] in [ftDate, ftDateTime];
end;
function TmdvDBF.GetFieldIsFloat(Index: Integer): Boolean;
begin
Result:= FieldType[Index] in [ftFloat, ftNumeric];
end;
function TmdvDBF.GetFieldIsInteger(Index: Integer): Boolean;
begin
Result:= (FieldType[Index] in [ftFloat, ftNumeric])and(FDBFFields[Index].Decimals = 0);
end;
function TmdvDBF.GetFieldIsMemo(Index: Integer): Boolean;
begin
Result:= FieldType[Index] in [ftMemo, ftGeneral, ftBinary, ftPicture];
end;
function TmdvDBF.GetFieldIsNull(Index: Integer): Boolean;
begin
Result:= ((FDBFFields[Index].FieldFlags and $02) = $02);
end;
function TmdvDBF.GetFieldIsString(Index: Integer): Boolean;
begin
Result:= FieldType[Index] in [ftCharacter, ftDate, ftLogical, ftFloat, ftNumeric, ftDateTime];
end;
function TmdvDBF.GetFieldLength(Index: Integer): Byte;
begin
Result:= FDBFFields[Index].FieldLength;
end;
function TmdvDBF.GetFieldName(Index: Integer): String;
begin
Result:= FDBFFields[Index].FieldName;
end;
function TmdvDBF.GetFieldNumber(NameField: String): Integer;
var i: Integer;
begin
Result:= -1;
for i:= 0 to FFieldsCount-1 do
if String(PChar(@FDBFFields[i].FieldName)) = NameField then begin
Result:= i; Break;
end;
end;
function TmdvDBF.GetFieldType(Index: Integer): TFieldType;
begin
case FDBFFields[Index].FieldType of
'C': if (FDBFFields[Index].FieldFlags and $04)=$04 then Result:= ftCharacterBin else Result:= ftCharacter;
'Y': Result:= ftCurrency;
'N': Result:= ftNumeric;
'F': Result:= ftFloat;
'D': Result:= ftDate;
'T': Result:= ftDateTime;
'B': Result:= ftBinary;
'I': Result:= ftInteger;
'L': Result:= ftLogical;
'M': if (FDBFFields[Index].FieldFlags and $04)=$04 then Result:= ftMemoBin else Result:= ftMemo;
'G': Result:= ftGeneral;
'P': Result:= ftPicture;
else Result:= ftUnknown;
end;
end;
function TmdvDBF.GetHasMemo: Boolean;
begin
Result:= FDBFHeader.TableFlags and $02 = $02;
end;
function TmdvDBF.InvertDWord(var Value: DWord): DWord;
type TB = array[0..3] of Byte;
begin
TB(Result)[0]:= TB(Value)[3];
TB(Result)[1]:= TB(Value)[2];
TB(Result)[2]:= TB(Value)[1];
TB(Result)[3]:= TB(Value)[0];
end;
function TmdvDBF.GetIsDelete: Boolean;
begin
Result:= False;
if not FActive then Exit;
Result:= (FRecordBuffer^[0] = $2A)
end;
function TmdvDBF.GetLastUpdated: TDateTime;
begin
EncodeDate(FDBFHeader.LastUpdated[1], FDBFHeader.LastUpdated[2], FDBFHeader.LastUpdated[3], Result);
end;
function TmdvDBF.GetMemoAsString(Index: Integer): String;
var P: Pointer;
Sz: DWord;
begin
Sz:= GetMemoValue(Index, P);
SetLength(Result, Sz);
if Sz > 0 then begin
Move(P^, Result[1], Sz);
DisposeMem(P);
end;
end;
function TmdvDBF.GetMemoValue(Index: Integer; var Value: Pointer): DWord;
var BlockNum, BlockSize, BlockCount: DWord;
k: Integer;
MemoType: TMemoType;
begin
Value:= nil; Result:= 0;
MemoType:= GetMemoInfo(Index, BlockNum, BlockSize, BlockCount, Result);
if (BlockCount = 0) or (BlockNum = 0) or (MemoType = mtUnknown) then Exit;
case MemoType of
mtFoxPro, mtdBaseIV: begin
FDBFMemoStream.Seek(BlockNum * BlockSize+SizeOf(TMemoBlockHeader), spBegin);
Value:= AllocMem(Result);
FDBFMemoStream.Read(Value^, Result);
end;
mtdBase: begin
FDBFMemoStream.Seek(BlockNum * BlockSize, spBegin);
Result:= 0;
repeat
inc(Result, BlockSize); ReallocMem(Value, Result+1);
FDBFMemoStream.Read(PChar(Value)[Result-512], BlockSize);
PChar(Value)[Result+1]:= #0;
k:= Pos(#$1A, PChar(Value));
if k>0 then begin
Result:= k-1;
ReallocMem(Value, Result);
Break;
end;
until FDBFMemoStream.Position >= FDBFMemoStream.Size;
end;
end;
end;
procedure TmdvDBF.GetValue(Index: Integer; var Value);
begin
try
Move(FRecordBuffer^[FDBFFields[Index].Address], Value, FDBFFields[Index].FieldLength);
except
FError:= eReadField;
end;
end;
procedure TmdvDBF.Last;
begin
SetCurrentRecord(FDBFHeader.RecordCount-1);
FBOF:= False; FEOF:= FCurrentRecord = FDBFHeader.RecordCount-1;
end;
procedure TmdvDBF.Next;
begin
SetCurrentRecord(FCurrentRecord+1);
FBOF:= False; FEOF:= FCurrentRecord = FDBFHeader.RecordCount-1;
end;
procedure TmdvDBF.Post;
begin
if FRecordModified then WriteRecord;
end;
procedure TmdvDBF.Prev;
begin
SetCurrentRecord(FCurrentRecord-1);
FBOF:= FCurrentRecord=0; FEOF:= False;
end;
function TmdvDBF.ReadFields: Boolean;
var DBFField: TDBFField;
Addr: DWord;
begin
try
Result:= True;
FFieldsCount:= 0;
FDBFFields:= AllocMem(SizeOf(TDBFField)*((FDBFHeader.HeaderLength - SizeOf(TDBFHeader)) div SizeOf(TDBFField)));
FDBFStream.Seek(SizeOf(TDBFHeader), spBegin); FHasMemo:= False;
Addr:= 1;
repeat
FDBFStream.Read(DBFField, SizeOf(TDBFField));
if DBFField.FieldName[1] <> #13 then begin
if DBFField.Address = 0 then DBFField.Address:= Addr;
FDBFFields[FFieldsCount]:= DBFField;
FHasMemo:= FHasMemo or FieldIsMemo[FFieldsCount];
inc(FFieldsCount);
end;
inc(Addr, DBFField.FieldLength);
until DBFField.FieldName[1] = #13;
except
Result:= False;
FError:= eDBFHeader;
end;
end;
procedure TmdvDBF.ReadRecord;
Begin
if not FActive then Exit;
try
FDBFStream.Seek(FDBFHeader.HeaderLength + FCurrentRecord*FDBFHeader.RecordLength, spBegin);
FDBFStream.Read(FRecordBuffer^, FDBFHeader.RecordLength);
FRecordModified := False;
except
FError:= eReadRecord;
end;
end;
procedure TmdvDBF.RefreshRecord;
begin
if not FActive then Exit;
ReadRecord;
end;
procedure TmdvDBF.SetActive(const Value: Boolean);
const ReadWrite: array [Boolean] of DWord = (ofOpenReadWrite, ofOpenRead);
// Stream: PStream;
begin
if Value = FActive then Exit;
if Value then begin
if not FileExists(FFileName) then begin FError:= eFileNotExist; Exit; end;
FDBFStream:= NewFileStream(FFileName, ReadWrite[FReadOnly] or ofShareDenyNone or{ofShareDenyWrite or }ofOpenExisting);
if FDBFStream.Handle = 0 then begin FError:= eFileOpen; Exit; end;
{FDBFStream:= NewMemoryStream;
Stream:= NewReadFileStream(FFileName);
FDBFStream.Size:= Stream.Size;
Stream.Read(FDBFStream.Memory^, Stream.Size);
Stream.Free;}
FDBFStream.Read(FDBFHeader, SizeOf(TDBFHeader));
if not ReadFields then Exit;
FRecordBuffer:= AllocMem(FDBFHeader.RecordLength);
if FHasMemo then begin
if not FileExists(FFileNameMemo) then begin
FFileNameMemo:= ChangeFileExt(FFileName, '.fpt');
if not FileExists(FFileNameMemo) then begin
FFileNameMemo:= ChangeFileExt(FFileName, '.dbt');
if not FileExists(FFileNameMemo) then begin
FFileNameMemo:= '';
end;
end;
end;
if FFileNameMemo <> '' then begin
FDBFMemoStream:= NewFileStream(FFileNameMemo, ReadWrite[FReadOnly] {or ofShareDenyWrite }or ofShareDenyNone or ofOpenExisting);
{FDBFMemoStream:= NewMemoryStream;
Stream:= NewReadFileStream(FFileNameMemo);
FDBFMemoStream.Size:= Stream.Size;
Stream.Read(FDBFMemoStream.Memory^, Stream.Size);
Stream.Free;}
if FDBFHeader.DBFType in [DBF_FoxPro2xMemo, DBF_VisualFoxPro, DBF_VisualFoxProInc] then FDBFMemoStream.Read(FFPTHeader, SizeOf(TFPTHeader))
else FDBFMemoStream.Read(FDBTHeader, SizeOf(TDBTHeader));
end
else FHasMemo:= False;
end;
FCurrentRecord:= 0; FDBFModified:= False; FRecordModified:= False;
FEOF:= False; FBOF:= False;
FActive := True;
end
else begin
if FAutoUpdate then Post;
if FDBFModified and not FReadOnly then begin
SetDate(FDBFHeader.LastUpdated);
FDBFStream.Seek(0, spBegin); FDBFStream.Write(FDBFHeader, SizeOf(TDBFHeader));
end;
DisposeMem(Pointer(FDBFFields));
DisposeMem(Pointer(FRecordBuffer));
FDBFStream.Free;
if FHasMemo then FDBFMemoStream.Free;
FFileNameMemo:= '';
FActive := False;
end
end;
procedure TmdvDBF.SetAsBoolean(Index: Integer; const Value: Boolean);
begin
if Value then AsString[Index]:= 'T' else AsString[Index]:= 'F';
end;
procedure TmdvDBF.SetAsDateTime(Index: Integer; const Value: TDateTime);
begin
AsString[Index]:= Date2StrFmt('yyyyMMdd', Value)+Time2StrFmt('HHmmss', Value);
end;
procedure TmdvDBF.SetAsFloat(Index: Integer; const Value: Double);
begin
AsString[Index]:= Double2Str(Value);
end;
procedure TmdvDBF.SetAsInteger(Index: Integer; const Value: Integer);
begin
AsString[Index]:= Int2Str(Value);
end;
procedure TmdvDBF.SetAsString(Index: Integer; const Value: String);
var S, SS, I: String;
ValidValue: Boolean;
lI, lp, lF, k: Integer;
D: TDateTime;
begin
try
S:= Value;
ValidValue:= False;
case FieldType[Index] of
ftCharacter: begin
ValidValue:= True;
end;
ftDate, ftDateTime: begin
D:= Str2DateTimeFmt('yyyyMMddHHmmss', Value); S:= Value;
ValidValue:= (Value = Date2StrFmt('yyyyMMdd', D)+Time2StrFmt('HHmmss', D));
end;
ftLogical: begin
ValidValue:= (Length(Value)=1);
if ValidValue then ValidValue:= Value[1] in ['T', 't', 'Y', 'y', 'F', 'f', 'N', 'n'];
end;
ftFloat, ftNumeric: begin
SS:= Trim(Value);
ValidValue:= Double2Str((Str2Double(SS))) = SS;
if ValidValue then begin
lP:= FDBFFields[Index].FieldLength - FDBFFields[Index].Decimals;
lI:= FDBFFields[Index].FieldLength - FDBFFields[Index].Decimals - 1;
lF:= FDBFFields[Index].Decimals;
if FDBFFields[Index].Decimals = 0 then begin
lI:= FDBFFields[Index].FieldLength; lF:= 0; lP:= 0;
end;
if FDBFFields[Index].FieldLength <= FDBFFields[Index].Decimals then begin
lI:= 0; lF:= FDBFFields[Index].FieldLength; lP:= 0;
end;
if lP > 0 then S:= '.' else S:='';
I:= Parse(SS, '.');
lP:= Min(Length(I), lI);
SetLength(I, lP);
for k:= lP to lI-1 do I:= ' '+I;
lP:= Min(Length(SS), lF);
SetLength(SS, lP);
for k:= lP to lF-1 do SS:= SS+'0';
S:= I+S+SS;
end;
end;
end;
if ValidValue then begin
FillMemory(@(FRecordBuffer^[FDBFFields[Index].Address]), FDBFFields[Index].FieldLength, Byte(' '));
SetString(Index, S);
end
else FError:= eInvalidValue;
except
FError:= eWriteField;
end;
end;
procedure TmdvDBF.SetCurrentRecord(const Value: DWord);
var Allowed: Boolean;
begin
if not FActive then Exit;
Allowed:= True;
if Assigned(FOnScroll) then FOnScroll(@Self, Allowed);
if not Allowed then Exit;
if FAutoUpdate then Post;
FCurrentRecord := Max(0, Min(FDBFHeader.RecordCount-1, Value));
ReadRecord;
end;
procedure TmdvDBF.SetFileName(const Value: String);
begin
if FFileName <> Value then begin
Active:= False;
FFileName := Value;
end;
end;
procedure TmdvDBF.SetIsDelete(const Value: Boolean);
const DelFlag: array [Boolean] of Byte = ($20, $2A);
var Allowed: Boolean;
begin
if not FActive then Exit;
Allowed:= True;
if Assigned(FOnDelete) then FOnDelete(@Self, Allowed);
if not Allowed then Exit;
FRecordBuffer^[0] := DelFlag[Value];
FRecordModified:= True;
end;
procedure TmdvDBF.SetMemoAsString(Index: Integer; Value: String);
begin
SetMemoValue(Index, PChar(Value), Length(Value), 1);
end;
procedure TmdvDBF.SetMemoValue(Index: Integer; Value: Pointer; ACount: DWord; ASigna: DWord = 0);
var BSize, BlockNum, BlockSize, BlockCount, Size, mh: DWord;
MemoType: TMemoType;
MemoBlockHeader: TMemoBlockHeader;
S: String;
begin
if FReadOnly then Exit;
MemoType:= GetMemoInfo(Index, BlockNum, BlockSize, BlockCount, Size);
if (BlockCount = 0) or (MemoType = mtUnknown) then Exit;
if MemoType <> mtdBase then mh:= SizeOf(TMemoBlockHeader) else mh:= 1;
BSize:= Size+mh;
BSize:= (BSize div BlockSize)*BlockSize + BlockSize*DWord(Ord((BSize mod BlockSize)>0));
if (BlockNum = 0) or (BSize < ACount + mh) then begin
BSize:= ACount + mh;
BSize:= (BSize div BlockSize)*BlockSize + BlockSize*DWord(Ord((BSize mod BlockSize)>0));
BlockNum:= BlockCount;
BlockCount:= BlockCount + BSize div BlockSize;
end;
FDBFMemoStream.Size:= BlockCount * BlockSize;
FDBFMemoStream.Seek(0, spBegin);
if MemoType = mtFoxPro then begin
MemoBlockHeader.Length:= InvertDWord(ACount);
MemoBlockHeader.BlockSignature:= InvertDWord(ASigna);
FFPTHeader.NextFree:= InvertDWord(BlockCount);
FDBFMemoStream.Write(FFPTHeader, SizeOf(TFPTHeader));
end
else begin
MemoBlockHeader.Length:= ACount + 8;
if MemoType = mtdBaseIV then MemoBlockHeader.BlockSignature:= $08FFFF
else MemoBlockHeader.BlockSignature:= ASigna;
FDBTHeader.NextFree:= BlockCount;
FDBFMemoStream.Write(FDBTHeader, SizeOf(TDBTHeader));
end;
if FDBFFields[Index].FieldLength = 4 then
SetValue(Index, BlockNum)
else begin
S:= Int2Str(BlockNum);
for mh:= Length(S)+1 to FDBFFields[Index].FieldLength do S:= ' '+S;
SetValue(Index, S[1]);
end;
Post;
case MemoType of
mtFoxPro, mtdBaseIV: begin
FDBFMemoStream.Seek(BlockNum * BlockSize, spBegin);
FDBFMemoStream.Write(MemoBlockHeader, SizeOf(TMemoBlockHeader));
FDBFMemoStream.Write(Value^, ACount);
end;
mtdBase: begin
FDBFMemoStream.Seek(BlockNum * BlockSize, spBegin);
FDBFMemoStream.Write(Value^, ACount);
S:= #$1A;
FDBFMemoStream.Write(S[1], 1);
end;
end;
end;
procedure TmdvDBF.SetString(Index: Integer; Value: String);
begin
try
Move(Value[1], FRecordBuffer^[FDBFFields[Index].Address], Min(Length(Value), FDBFFields[Index].FieldLength));
FDBFModified:= True;
FRecordModified:= True;
except
FError:= eWriteField;
end;
end;
procedure TmdvDBF.SetValue(Index: Integer; var Value);
begin
try
Move(Value, FRecordBuffer^[FDBFFields[Index].Address], FDBFFields[Index].FieldLength);
FDBFModified:= True;
FRecordModified:= True;
except
FError:= eWriteField;
end;
end;
procedure TmdvDBF.WriteRecord;
Begin
if not FActive or FReadOnly then Exit;
try
FDBFStream.Seek(FDBFHeader.HeaderLength + FCurrentRecord*FDBFHeader.RecordLength, spBegin);
FDBFStream.Write(FRecordBuffer^, FDBFHeader.RecordLength);
FRecordModified := False;
FDBFModified:= True;
except
FError:= eWriteRecord;
end;
end;
function TmdvDBF.GetAsText(Index: Integer): String;
begin
Result:= '';
try
if not FieldIsString[Index] then Exit;
SetLength(Result, FDBFFields[Index].FieldLength);
Move(FRecordBuffer^[FDBFFields[Index].Address], Result[1], FDBFFields[Index].FieldLength);
except
FError:= eReadField;
end;
end;
procedure TmdvDBF.SetAsText(Index: Integer; Value: String);
begin
try
FillMemory(@(FRecordBuffer^[FDBFFields[Index].Address]), FDBFFields[Index].FieldLength, Byte(' '));
SetValue(Index, Value[1]);
except
FError:= eWriteField;
end;
end;
function TmdvDBF.GetMemoInfo(Index: Integer; var ABlockNum, ABlockSize, ABlockCount, ASize: DWord): TMemoType;
var S: String;
MemoBlockHeader: TMemoBlockHeader;
begin
ABlockNum:=0; ABlockSize:=0; ABlockCount:=0; ASize:= 0;
Result:= mtUnknown;
if not FieldIsMemo[Index] then Exit;
if FDBFFields[Index].FieldLength = 4 then
Move(FRecordBuffer^[FDBFFields[Index].Address], ABlockNum, 4)
else begin
SetLength(S, FDBFFields[Index].FieldLength);
Move(FRecordBuffer^[FDBFFields[Index].Address], S[1], FDBFFields[Index].FieldLength);
ABlockNum:= Str2Int(Trim(S));
end;
if FDBFHeader.DBFType in [DBF_FoxPro2xMemo, DBF_VisualFoxPro, DBF_VisualFoxProInc] then Result:= mtFoxPro;
if FDBFHeader.DBFType in [DBF_dBaseIV, DBF_dBaseIVSQLtable, DBF_dBaseIVSQLsystem, DBF_dBaseIVSQLtableMemo, DBF_dBaseIVMemo, DBF_dBaseV] then Result:= mtdBaseIV;
if FDBFHeader.DBFType in [DBF_FoxBASE, DBF_FoxBASE_, DBF_dBaseIIIplus, DBF_dBaseIIIplusMemo] then Result:= mtdBase;
ABlockSize:= 512;
ABlockCount:= FDBTHeader.NextFree;
ASize:= 0;
if Result = mtFoxPro then begin
ABlockSize:= System.Swap(FFPTHeader.BlockSize);
ABlockCount:= InvertDWord(FFPTHeader.NextFree);
end;
if Result = mtdBaseIV then begin
ABlockSize:= FDBTHeader.BlockSize;
end;
if ABlockSize = 0 then ABlockSize:= 512;
if ABlockNum = 0 then Exit;
FDBFMemoStream.Seek(ABlockNum * ABlockSize, spBegin);
FDBFMemoStream.Read(MemoBlockHeader, SizeOf(TMemoBlockHeader));
if Result = mtFoxPro then ASize:= InvertDWord(MemoBlockHeader.Length);
if Result = mtdBaseIV then ASize:= MemoBlockHeader.Length - 8;
end;
procedure TmdvDBF.PackDBF;
var ReadPos, WritePos, Rec, RecCount: DWord;
begin
if FReadOnly then Exit;
Post;
ReadPos:= FDBFHeader.HeaderLength; WritePos:= FDBFHeader.HeaderLength;
Rec:= 0; RecCount:= 0;
while Rec < FDBFHeader.RecordCount do begin
FDBFStream.Seek(ReadPos, spBegin);
FDBFStream.Read(FRecordBuffer^, FDBFHeader.RecordLength);
inc(ReadPos, FDBFHeader.RecordLength);
inc(Rec);
if FRecordBuffer^[0] <> $2A then begin
FDBFStream.Seek(WritePos, spBegin);
FDBFStream.Write(FRecordBuffer^, FDBFHeader.RecordLength);
inc(WritePos, FDBFHeader.RecordLength);
inc(RecCount);
end
else FDBFModified:= True;
end;
FDBFStream.Size:= WritePos;
FDBFHeader.RecordCount := RecCount;
FDBFStream.Seek(0, spBegin);
FDBFStream.Write(FDBFHeader, SizeOf(TDBFHeader));
CurrentRecord:= 0;
end;
function TmdvDBF.Locate(AFieldName, AValue: String): Boolean;
var i, FieldNum: Integer;
begin
Result:= False;
FieldNum:= FieldNumber[AFieldName];
if FieldNum<0 then Exit;
for i:= 0 to RecordCount-1 do begin
CurrentRecord:= i;
if IsDelete then Continue;
Result:= AsString[FieldNum] = AValue;
if Result then Break;
end;
end;
function TmdvDBF.Locate(AFieldNames, AValues: array of String): Boolean;
var i, j: Integer;
FieldNums: array of Integer;
begin
Result:= False;
SetLength(FieldNums, Min(Length(AFieldNames), Length(AValues)));
try
for j:= Low(FieldNums) to High(FieldNums) do begin
FieldNums[j]:= FieldNumber[AFieldNames[j]];
if FieldNums[j]<0 then Exit;
end;
for i:= 0 to RecordCount-1 do begin
CurrentRecord:= i;
if IsDelete then Continue;
Result:= True;
for j:= Low(FieldNums) to High(FieldNums) do
Result:= Result and (AsString[FieldNums[j]] = AValues[j]);
if Result then Break;
end;
finally
SetLength(FieldNums, 0);
end;
end;
function TmdvDBF.GetAsBooleanByName(AFieldName: String): Boolean;
begin
Result:= AsBoolean[FieldNumber[AFieldName]];
end;
function TmdvDBF.GetAsDateTimeByName(AFieldName: String): TDateTime;
begin
Result:= AsDateTime[FieldNumber[AFieldName]];
end;
function TmdvDBF.GetAsFloatByName(AFieldName: String): Double;
begin
Result:= AsFloat[FieldNumber[AFieldName]];
end;
function TmdvDBF.GetAsIntegerByName(AFieldName: String): Integer;
begin
Result:= AsInteger[FieldNumber[AFieldName]];
end;
function TmdvDBF.GetAsStringByName(AFieldName: String): String;
begin
Result:= AsString[FieldNumber[AFieldName]];
end;
function TmdvDBF.GetAsTextByName(AFieldName: String): String;
begin
Result:= AsText[FieldNumber[AFieldName]];
end;
procedure TmdvDBF.SetAsBooleanByName(AFieldName: String; const Value: Boolean);
begin
AsBoolean[FieldNumber[AFieldName]]:= Value;
end;
procedure TmdvDBF.SetAsDateTimeByName(AFieldName: String; const Value: TDateTime);
begin
AsDateTime[FieldNumber[AFieldName]]:= Value;
end;
procedure TmdvDBF.SetAsFloatByName(AFieldName: String; const Value: Double);
begin
AsFloat[FieldNumber[AFieldName]]:= Value;
end;
procedure TmdvDBF.SetAsIntegerByName(AFieldName: String; const Value: Integer);
begin
AsInteger[FieldNumber[AFieldName]]:= Value;
end;
procedure TmdvDBF.SetAsStringByName(AFieldName: String; const Value: String);
begin
AsString[FieldNumber[AFieldName]]:= Value;
end;
procedure TmdvDBF.SetAsTextByName(AFieldName: String; const Value: String);
begin
AsText[FieldNumber[AFieldName]]:= Value;
end;
function TmdvDBF.GetMemoAsStringByName(AFieldName: String): String;
begin
Result:= MemoAsString[FieldNumber[AFieldName]];
end;
procedure TmdvDBF.SetMemoAsStringByName(AFieldName: String; const Value: String);
begin
MemoAsString[FieldNumber[AFieldName]]:= Value;
end;
function TmdvDBF.MaxOfField(AFieldName: String): Integer;
var i, FieldNum, k: Integer;
begin
Result:= 0;
FieldNum:= FieldNumber[AFieldName];
k:= CurrentRecord;
if FieldNum<0 then Exit;
for i:= 0 to RecordCount-1 do begin
CurrentRecord:= i;
if IsDelete then Continue;
Result:= Max(Result, AsInteger[FieldNum]);
end;
CurrentRecord:= k;
end;
function TmdvDBF.NewDbf(AFileName: String; DescriptionDBF: String): Boolean;
var IsMemo: Boolean;
i, _FieldsCount, _RecordLength, _BlockSize: Word;
_CodePage: Byte;
_DBFHeader: TDBFHeader;
_DBFFields: PDBFFields;
_DBTHeader: TDBTHeader;
S, SS: String;
Addr: DWord;
Stream: PStream;
begin
Active:= False;
Result:= False;
S:= Parse(DescriptionDBF, #1);
_CodePage:= Str2Int(Parse(S, #2));
_BlockSize:= Str2Int(S);
_FieldsCount:= 0; S:= DescriptionDBF;
while S <> '' do begin
Parse(S, #1); Inc(_FieldsCount);
end;
if (_FieldsCount = 0) or (_FieldsCount > 255) then Exit;
GetMem(_DBFFields, _FieldsCount*SizeOf(TDBFField));
try
IsMemo:= False;
i:= 0; Addr:= 1; _RecordLength:= 1;
while DescriptionDBF <> '' do begin
S:= Parse(DescriptionDBF, #1);
FillChar(_DBFFields^[i], SizeOf(TDBFFields), 0);
with _DBFFields[i] do begin
// Íàçâàíèå ïîëÿ
SS:= Parse(S, #2);
if (SS = '') or (Length(SS) > 10) then Exit;
Move(SS[1], FieldName, Length(SS));
// Òèï ïîëÿ 'C' - Character; 'N' - Numeric; 'D' - Date; 'B' - Binary; 'L' - Logical, 'M' - Memo
SS:= Parse(S, #2);
if (SS = '') or (Length(SS) <> 1) then Exit;
FieldType:= SS[1];
// Äëèíà ïîëÿ (â áàéòàõ)
FieldLength:= Str2Int(Parse(S, #2));
case FieldType of
'C': if (FieldLength = 0) or (FieldLength > 254) then Exit;
'N': if (FieldLength = 0) or (FieldLength > 20) then Exit;
'D': FieldLength:= 8;
'L': FieldLength:= 1;
'B', 'M': begin
FieldLength:= 10;
IsMemo:= True;
end;
else Exit;
end;
// Äëèíà äåñÿòè÷íîé ÷àñòè
SS:= Parse(S, #2);
if FieldType = 'N' then
Decimals:= Max(0, Min(FieldLength-1, Str2Int(SS)));
// Ôëàã ïîëÿ
// if FieldType in ['B', 'M'] then FieldFlags:= $04;
// if FieldType = 'C' then FieldFlags:= FieldFlags or $02;
Address:= Addr;
inc(_RecordLength, FieldLength);
inc(Addr, FieldLength);
end;
inc(i);
end;
FillChar(_DBFHeader, SizeOf(_DBFHeader), 0);
with _DBFHeader do begin
if IsMemo then DBFType:= DBF_dBaseIVMemo else DBFType:= DBF_dBaseIV; // Òèï ôàéëà (DBF_xxx)
SetDate(LastUpdated); // Äàòà ïîñëåäíåãî îáíîâëåíèÿ â ôîðìàòå YYMMDD
RecordCount:= 0; // Êîëè÷åñòâî çàïèñåé â òàáëèöå
HeaderLength:= SizeOf(_DBFHeader) + _FieldsCount * SizeOf(TDBFField) + 1; // Êîëè÷åñòâî áàéò, çàíèìàåìûõ çàãîëîâêîì
RecordLength:= _RecordLength; // Êîëè÷åñòâî áàéò, çàíèìàåìûõ çàïèñüþ
//Reserved_1: array [1..16] of Byte; // 3-Çàðåçåðâèðîâàííàÿ îáëàñòü; 13 - Çàðåçåðâèðîâàíî äëÿ ñåòåâîé âåðñèè dBASE III PLUS
if IsMemo then TableFlags:= $02; // $01 - file has a structural .cdx; $02 - file has a Memo field; $04 - file is a database (.dbc). This byte can contain the sum of any of the above values. For example, the value 0x03 indicates the table has a structural .cdx and a Memo field.
CodePage:= _CodePage; // Êîäîâàÿ ñòðàíèöà
//Reserved_2: Word; // Çàðåçåðâèðîâàííàÿ îáëàñòü
end;
Stream:= NewWriteFileStream(AFileName);
Stream.Size:= 0;
Stream.Write(_DBFHeader, SizeOf(_DBFHeader));
for i:= 0 to _FieldsCount-1 do
Stream.Write(_DBFFields[i], SizeOf(TDBFFields));
S:= #$0D#$1A;
Stream.Write(S[1], 2);
Stream.Free;
if IsMemo then begin
_BlockSize:= Max(_BlockSize, SizeOf(_DBTHeader));
FillChar(_DBTHeader, SizeOf(_DBTHeader), 0);
with _DBTHeader do begin
NextFree:= 512 div _BlockSize + Ord(512 mod _BlockSize > 0);
BlockSize:= _BlockSize;
end;
end;
Stream:= NewWriteFileStream(ChangeFileExt(AFileName, '.dbt'));
Stream.Size:= 0;
Stream.Write(_DBTHeader , SizeOf(_DBTHeader));
Stream.Size:= _DBTHeader.NextFree*_DBTHeader.BlockSize;
Stream.Free;
finally
FreeMem(_DBFFields);
end;
FFileName:= AFileName;
Active:= True;
Result:= True;
end;
procedure TmdvDBF.SetDate(var ADate: TYYMMDD);
var Y, M, D: Word;
begin
DecodeDate(Now, Y, M, D);
ADate[1]:= Lo(Y mod 100); ADate[2]:= Lo(M); ADate[3]:= Lo(D);
end;
end.