📄 dbf_dbffile.pas
字号:
GetMem(pBuff, RecordSize);
BlobStream := TMemoryStream.Create;
OldIndexFiles := TStringList.Create;
// if restructure, we need memory for dest buffer, otherwise use source
if DbfFieldDefs = nil then
pDestBuff := pBuff
else
GetMem(pDestBuff, DestDbfFile.RecordSize);
// let the games begin!
try
{$ifdef USE_CACHE}
BufferAhead := true;
DestDbfFile.BufferAhead := true;
{$endif}
lWRecNo := 1;
for lRecNo := 1 to RecordCount do
begin
// read record from original dbf
ReadRecord(lRecNo, pBuff);
// copy record?
if (pBuff^ <> '*') or not Pack then
begin
// if restructure, initialize dest
if DbfFieldDefs <> nil then
begin
DestDbfFile.InitRecord(pDestBuff);
// copy deleted mark (the first byte)
pDestBuff^ := pBuff^;
end;
if (DbfFieldDefs <> nil) or (FMemoFile <> nil) then
begin
// copy fields
for lFieldNo := 0 to DestFieldDefs.Count-1 do
begin
TempDstDef := DestFieldDefs.Items[lFieldNo];
// handle blob fields differently
// don't try to copy new blob fields!
// DbfFieldDefs = nil -> pack only
// TempDstDef.CopyFrom >= 0 -> copy existing (blob) field
if TempDstDef.IsBlob and ((DbfFieldDefs = nil) or (TempDstDef.CopyFrom >= 0)) then
begin
// get current blob blockno
GetFieldData(lFieldNo, ftInteger, pBuff, @lBlobPageNo, false);
// valid blockno read?
if lBlobPageNo > 0 then
begin
BlobStream.Clear;
FMemoFile.ReadMemo(lBlobPageNo, BlobStream);
BlobStream.Position := 0;
// always append
DestDbfFile.FMemoFile.WriteMemo(lBlobPageNo, 0, BlobStream);
end;
// write new blockno
DestDbfFile.SetFieldData(lFieldNo, ftInteger, @lBlobPageNo, pDestBuff, false);
end else if (DbfFieldDefs <> nil) and (TempDstDef.CopyFrom >= 0) then
begin
// copy content of field
with RestructFieldInfo[lFieldNo] do
Move(pBuff[SourceOffset], pDestBuff[DestOffset], Size);
end;
end;
end;
// write record
DestDbfFile.WriteRecord(lWRecNo, pDestBuff);
// update indexes
for I := 0 to DestDbfFile.IndexFiles.Count - 1 do
TIndexFile(DestDbfFile.IndexFiles.Items[I]).Insert(lWRecNo, pDestBuff);
// go to next record
Inc(lWRecNo);
end;
end;
{$ifdef USE_CACHE}
BufferAhead := false;
DestDbfFile.BufferAhead := false;
{$endif}
// save index filenames
for I := 0 to FIndexFiles.Count - 1 do
OldIndexFiles.Add(TIndexFile(IndexFiles[I]).FileName);
// close dbf
Close;
// if restructure -> rename the old dbf files
// if pack only -> delete the old dbf files
DestDbfFile.Rename(FileName, OldIndexFiles, DbfFieldDefs = nil);
// we have to reinit fielddefs if restructured
Open;
// crop deleted records
RecordCount := lWRecNo - 1;
// update date/time stamp, recordcount
PDbfHdr(Header)^.RecordCount := RecordCount;
WriteHeader;
finally
// close temporary file
FreeAndNil(DestDbfFile);
// free mem
FreeAndNil(OldIndexFiles);
FreeMem(pBuff);
FreeAndNil(BlobStream);
FreeMem(RestructFieldInfo);
if DbfFieldDefs <> nil then
FreeMem(pDestBuff);
end;
end;
procedure TDbfFile.RegenerateIndexes;
var
lIndexNo: Integer;
begin
// recreate every index in every file
for lIndexNo := 0 to FIndexFiles.Count-1 do
begin
PackIndex(TIndexFile(FIndexFiles.Items[lIndexNo]), EmptyStr);
end;
end;
function TDbfFile.GetFieldInfo(FieldName: string): TDbfFieldDef;
var
I: Integer;
lfi: TDbfFieldDef;
begin
FieldName := AnsiUpperCase(FieldName);
for I := 0 to FFieldDefs.Count-1 do
begin
lfi := TDbfFieldDef(FFieldDefs.Items[I]);
if lfi.fieldName = FieldName then
begin
Result := lfi;
exit;
end;
end;
Result := nil;
end;
// NOTE: Dst may be nil!
function TDbfFile.GetFieldData(Column: Integer; DataType: TFieldType;
Src, Dst: Pointer; NativeFormat: boolean): Boolean;
var
TempFieldDef: TDbfFieldDef;
begin
TempFieldDef := TDbfFieldDef(FFieldDefs.Items[Column]);
Result := GetFieldDataFromDef(TempFieldDef, DataType, Src, Dst, NativeFormat);
end;
// NOTE: Dst may be nil!
function TDbfFile.GetFieldDataFromDef(AFieldDef: TDbfFieldDef; DataType: TFieldType;
Src, Dst: Pointer; NativeFormat: boolean): Boolean;
var
FieldOffset, FieldSize: Integer;
// s: string;
ldd, ldm, ldy, lth, ltm, lts: Integer;
date: TDateTime;
timeStamp: TTimeStamp;
asciiContents: boolean;
{$ifdef SUPPORT_INT64}
function GetInt64FromStrLength(Src: Pointer; Size: Integer; Default: Int64): Int64;
var
endChar: Char;
Code: Integer;
begin
// save Char at pos term. null
endChar := (PChar(Src) + Size)^;
(PChar(Src) + Size)^ := #0;
// convert
Val(PChar(Src), Result, Code);
// check success
if Code <> 0 then Result := Default;
// restore prev. ending Char
(PChar(Src) + Size)^ := endChar;
end;
{$endif}
procedure CorrectYear(var wYear: Integer);
var wD, wM, wY, CenturyBase: Word;
{$ifndef DELPHI_5}
// Delphi 3 standard-behavior no change possible
const TwoDigitYearCenturyWindow= 0;
{$endif}
begin
if wYear >= 100 then
Exit;
DecodeDate(Date, wY, wm, wD);
// use Delphi-Date-Window
CenturyBase := wY{must be CurrentYear} - TwoDigitYearCenturyWindow;
Inc(wYear, CenturyBase div 100 * 100);
if (TwoDigitYearCenturyWindow > 0) and (wYear < CenturyBase) then
Inc(wYear, 100);
end;
procedure SaveDateToDst;
begin
if not NativeFormat then
begin
// Delphi 5 requests a TDateTime
PDateTime(Dst)^ := date;
end else begin
// Delphi 3 and 4 request a TDateTimeRec
// date is TTimeStamp.date
// datetime = msecs == BDE timestamp as we implemented it
if DataType = ftDateTime then
begin
PDateTimeRec(Dst)^.DateTime := date;
end else begin
PLongInt(Dst)^ := DateTimeToTimeStamp(date).Date;
end;
end;
end;
begin
// test if non-nil source (record buffer)
if Src = nil then
begin
Result := false;
exit;
end;
// check Dst = nil, called with dst = nil to check empty field
if (FNullField <> nil) and (Dst = nil) and (AFieldDef.NullPosition >= 0) then
begin
// go to byte with null flag of this field
Src := PChar(Src) + FNullField.Offset + (AFieldDef.NullPosition shr 3);
Result := (PByte(Src)^ and (1 shl (AFieldDef.NullPosition and $7))) <> 0;
exit;
end;
FieldOffset := AFieldDef.Offset;
FieldSize := AFieldDef.Size;
Src := PChar(Src) + FieldOffset;
asciiContents := false;
Result := true;
// field types that are binary and of which the fieldsize should not be truncated
case AFieldDef.NativeFieldType of
'+', 'I':
begin
if FDbfVersion <> xFoxPro then
begin
Result := PDWord(Src)^ <> 0;
if Result and (Dst <> nil) then
begin
PDWord(Dst)^ := SwapIntBE(PDWord(Src)^);
if Result then
PInteger(Dst)^ := Integer(PDWord(Dst)^ xor $80000000);
end;
end else begin
Result := true;
if Dst <> nil then
PInteger(Dst)^ := SwapIntLE(PInteger(Src)^);
end;
end;
'O':
begin
{$ifdef SUPPORT_INT64}
Result := PInt64(Src)^ <> 0;
if Result and (Dst <> nil) then
begin
SwapInt64BE(Src, Dst);
if PInt64(Dst)^ > 0 then
PInt64(Dst)^ := not PInt64(Dst)^
else
PDouble(Dst)^ := PDouble(Dst)^ * -1;
end;
{$endif}
end;
'@':
begin
Result := (PInteger(Src)^ <> 0) and (PInteger(PChar(Src)+4)^ <> 0);
if Result and (Dst <> nil) then
begin
SwapInt64BE(Src, Dst);
if FDateTimeHandling = dtBDETimeStamp then
date := BDETimeStampToDateTime(PDouble(Dst)^)
else
date := PDateTime(Dst)^;
SaveDateToDst;
end;
end;
'T':
begin
// all binary zeroes -> empty datetime
{$ifdef SUPPORT_INT64}
Result := PInt64(Src)^ <> 0;
{$else}
Result := (PInteger(Src)^ <> 0) or (PInteger(PChar(Src)+4)^ <> 0);
{$endif}
if Result and (Dst <> nil) then
begin
timeStamp.Date := SwapIntLE(PInteger(Src)^) - JulianDateDelta;
timeStamp.Time := SwapIntLE(PInteger(PChar(Src)+4)^);
date := TimeStampToDateTime(timeStamp);
SaveDateToDst;
end;
end;
'Y':
begin
{$ifdef SUPPORT_INT64}
Result := true;
if Dst <> nil then
begin
PInt64(Dst)^ := SwapIntLE(PInt64(Src)^);
if DataType = ftCurrency then
PDouble(Dst)^ := PInt64(Dst)^ / 10000.0;
end;
{$endif}
end;
'B': // foxpro double
begin
if FDbfVersion = xFoxPro then
begin
Result := true;
if Dst <> nil then
PInt64(Dst)^ := SwapIntLE(PInt64(Src)^);
end else
asciiContents := true;
end;
'M':
begin
if FieldSize = 4 then
begin
Result := PInteger(Src)^ <> 0;
if Dst <> nil then
PInteger(Dst)^ := SwapIntLE(PInteger(Src)^);
end else
asciiContents := true;
end;
else
asciiContents := true;
end;
if asciiContents then
begin
// SetString(s, PChar(Src) + FieldOffset, FieldSize );
// s := {TrimStr(s)} TrimRight(s);
// truncate spaces at end by shortening fieldsize
while (FieldSize > 0) and ((PChar(Src) + FieldSize - 1)^ = ' ') do
dec(FieldSize);
// if not string field, truncate spaces at beginning too
if DataType <> ftString then
while (FieldSize > 0) and (PChar(Src)^ = ' ') do
begin
inc(PChar(Src));
dec(FieldSize);
end;
// return if field is empty
Result := FieldSize > 0;
if Result and (Dst <> nil) then // data not needed if Result= false or Dst=nil
case DataType of
ftBoolean:
begin
// in DBase- FileDescription lowercase t is allowed too
// with asking for Result= true s must be longer then 0
// else it happens an AV, maybe field is NULL
if (PChar(Src)^ = 'T') or (PChar(Src)^ = 't') then
PWord(Dst)^ := 1
else
PWord(Dst)^ := 0;
end;
ftSmallInt:
PSmallInt(Dst)^ := GetIntFromStrLength(Src, FieldSize, 0);
{$ifdef SUPPORT_INT64}
ftLargeInt:
PLargeInt(Dst)^ := GetInt64FromStrLength(Src, FieldSize, 0);
{$endif}
ftInteger:
PInteger(Dst)^ := GetIntFromStrLength(Src, FieldSize, 0);
ftFloat, ftCurrency:
PDouble(Dst)^ := DbfStrToFloat(Src, FieldSize);
ftDate, ftDateTime:
begin
// get year, month, day
ldy := GetIntFromStrLength(PChar(Src) + 0, 4, 1);
ldm := GetIntFromStrLength(PChar(Src) + 4, 2, 1);
ldd := GetIntFromStrLength(PChar(Src) + 6, 2, 1);
//if (ly<1900) or (ly>2100) then ly := 1900;
//Year from 0001 to 9999 is possible
//everyting else is an error, an empty string too
//Do DateCorrection with Delphis possibillities for one or two digits
if (ldy < 100) and (PChar(Src)[0] = #32) and (PChar(Src)[1] = #32) then
CorrectYear(ldy);
try
date := EncodeDate(ldy, ldm, ldd);
except
date := 0;
end;
// time stored too?
if (AFieldDef.FieldType = ftDateTime) and (DataType = ftDateTime) then
begin
// get hour, minute, second
lth := GetIntFromStrLength(PChar(Src) + 8, 2, 1);
ltm := GetIntFromStrLength(PChar(Src) + 10, 2, 1);
lts := GetIntFromStrLength(PChar(Src) + 12, 2, 1);
// encode
try
date := date + EncodeTime(lth, ltm, lts, 0);
except
date := 0;
end;
end;
SaveDateToDst;
end;
ftString:
StrLCopy(Dst, Src, FieldSize);
end else begin
case DataType of
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -