📄 kbmmemcsvstreamformat.pas
字号:
end;
Result[I]:=Char(Act and 63+32);
end;
Result[Len]:=Char(Act shr 6+32);
end
else
Result:='';
end;
// Decode BASE64 string.
function Base64ToString(const Source:string): string;
var
Act: Word;
Bits,I,P,Len: Integer;
begin
Len:=(Length(Source)*3) div 4;
SetLength(Result,Len);
Bits:=0;
Act:=0;
P:=1;
{$IFDEF DOTNET}
for I:=1 to Length(Source) do
begin
Act:=Act or (Ord(Source[I])-32) shl Bits;
if Bits>=2 then
begin
Result[P]:=Char(Act and $FF);
Inc(P);
Act:=Act shr 8;
Dec(Bits,2);
end
else
Inc(Bits,6);
end;
{$ELSE}
for I:=1 to system.Length(Source) do
begin
Act:=Act or (Ord(Source[I])-32) shl Bits;
if Bits>=2 then
begin
Result[P]:=Char(Act and $FF);
Inc(P);
Act:=Act shr 8;
Dec(Bits,2);
end
else
Inc(Bits,6);
end;
{$ENDIF}
end;
// Quote a string.
function QuoteString(const Source:string; Quote:char):string;
begin
if Quote=#0 then Result:=Source
{$IFDEF DOTNET}
else Result:=QuotedStr(Source,Quote);
{$ELSE}
else Result:=AnsiQuotedStr(Source,Quote);
{$ENDIF}
end;
// Extract a quoted string.
function ExtractQuoteString(const Source:string; Quote:char):string;
{$IFDEF DOTNET}
var
p:IntPtr;
i,l,lq:integer;
begin
p:=Marshal.StringToHGlobalAnsi(Source);
try
if Quote=#0 then
Result:=Source
else
begin
Result := '';
if Source.Chars[0] <> quote then exit;
l:= Source.Length;
lq:=0;
for i:=l-1 downto 0 do
begin
if Source.Chars[i]=quote then
begin
lq:=i;
break;
end;
end;
if lq = 0 then lq:=l-1;
i:=0;
while i<lq do
begin
if Marshal.ReadByte(p,i)<>Byte(Quote) then
begin
result:=result+char(Marshal.ReadByte(p,i));
end else
begin
inc(i);
if i>=lq then break;
result:=result+char(Marshal.ReadByte(p,i));
end;
inc(i);
end;
end;
finally
Marshal.FreeHGlobal(p);
end;
end;
{$ELSE}
var
p:PChar;
begin
p:=PChar(Source);
if Quote=#0 then Result:=Source
else Result:=AnsiExtractQuotedStr(p,Quote);
end;
{$ENDIF}
// TKbmCustomCSVStreamFormat
//*******************************************************************
constructor TkbmCustomCSVStreamFormat.Create(AOwner:TComponent);
begin
inherited;
FCSVQuote:='"';
FCSVFieldDelimiter:=',';
FCSVRecordDelimiter:=',';
FCSVTrueString:='True';
FCSVFalseString:='False';
FsfLocalFormat:=[];
FsfPlaceHolders:=[];
end;
function TkbmCustomCSVStreamFormat.GetVersion:string;
begin
Result:='3.00';
end;
procedure TkbmCUstomCSVStreamFormat.Assign(Source:TPersistent);
begin
if Source is TkbmCustomCSVStreamFormat then
begin
CSVQuote:=TkbmCustomCSVStreamFormat(Source).CSVQuote;
CSVFieldDelimiter:=TkbmCustomCSVStreamFormat(Source).CSVFieldDelimiter;
CSVRecordDelimiter:=TkbmCustomCSVStreamFormat(Source).CSVRecordDelimiter;
CSVTrueString:=TkbmCustomCSVStreamFormat(Source).CSVTrueString;
CSVFalseString:=TkbmCustomCSVStreamFormat(Source).CSVFalseString;
sfLocalFormat:=TkbmCustomCSVStreamFormat(Source).sfLocalFormat;
sfPlaceHolders:=TkbmCustomCSVStreamFormat(Source).sfPlaceHolders;
end;
inherited;
end;
procedure TkbmCustomCSVStreamFormat.SetCSVFieldDelimiter(Value:char);
begin
if Value<>#0 then FCSVFieldDelimiter:=Value;
end;
procedure TkbmCustomCSVStreamFormat.BeforeSave(ADataset:TkbmCustomMemTable);
begin
// Check if trying to save deltas in CSV format. Not supported.
if sfSaveDeltas in sfDeltas then
raise EMemTableError.Create(kbmSavingDeltasBinary);
inherited;
// Setup standard layout for data.
Ods:=DateSeparator;
Oms:=DecimalSeparator;
Ots:=TimeSeparator;
Oths:=ThousandSeparator;
Ocf:=CurrencyFormat;
Onf:=NegCurrFormat;
Osdf:=ShortDateFormat;
Ocs:=CurrencyString;
if not (sfSaveLocalFormat in FsfLocalFormat) then
begin
DateSeparator:='/';
TimeSeparator:=':';
ThousandSeparator:=',';
DecimalSeparator:='.';
ShortDateFormat:='dd/mm/yyyy';
CurrencyString:='';
CurrencyFormat:=0;
NegCurrFormat:=1;
end;
end;
procedure TkbmCustomCSVStreamFormat.AfterSave(ADataset:TkbmCustomMemTable);
begin
// Restore locale setup.
DateSeparator:=Ods;
DecimalSeparator:=Oms;
TimeSeparator:=Ots;
ThousandSeparator:=Oths;
CurrencyFormat:=Ocf;
NegCurrFormat:=Onf;
ShortDateFormat:=Osdf;
CurrencyString:=Ocs;
inherited;
end;
procedure TkbmCustomCSVStreamFormat.SaveDef(ADataset:TkbmCustomMemTable);
var
i,l:integer;
nf:integer;
s:string;
begin
if not (sfSaveDef in sfDef) then exit;
// Setup flags for fields to save.
nf:=ADataSet.Fieldcount;
{$IFNDEF CSV_FILE_1XX_COMPATIBILITY}
// Write file version.
{$IFDEF DOTNET}
s:=QuoteString(kbmFileVersionMagic,FCSVQuote)+FCSVFieldDelimiter+QuoteString(IntToStr(kbmCSVFileVersion),FCSVQuote)+#13+#10;
l:=length(s);
WorkStream.WriteBuffer (BytesOf(s), l);
{$ELSE}
s:=QuoteString(PChar(kbmFileVersionMagic),FCSVQuote)+FCSVFieldDelimiter+QuoteString(IntToStr(kbmCSVFileVersion),FCSVQuote)+#13+#10;
l:=length(s);
WorkStream.WriteBuffer(Pointer(s)^, l);
{$ENDIF}
{$ENDIF}
// Write header.
{$IFDEF DOTNET}
s:=QuoteString(kbmTableDefMagicStart,FCSVQuote)+#13+#10;
l:=length(s);
WorkStream.WriteBuffer(bytesOf(s), l);
{$ELSE}
s:=QuoteString(PChar(kbmTableDefMagicStart),FCSVQuote)+#13+#10;
l:=length(s);
WorkStream.WriteBuffer(Pointer(s)^, l);
{$ENDIF}
// Write fielddefinitions.
for i:=0 to nf-1 do
begin
if (SaveFields[i]>=0) or (sfSavePlaceHolders in sfPlaceHolders) then
begin
s:=ADataSet.Fields[i].FieldName+'='+
FieldTypeNames[ADataSet.Fields[i].DataType]+','+
inttostr(ADataSet.Fields[i].Size)+','+
QuoteString(ADataSet.Fields[i].DisplayName,'"')+','+
QuoteString(ADataSet.Fields[i].EditMask,'"')+','+
inttostr(ADataSet.Fields[i].DisplayWidth);
if ADataSet.Fields[i].Required then s:=s+',REQ';
if ADataSet.Fields[i].ReadOnly then s:=s+',RO';
if not ADataSet.Fields[i].Visible then s:=s+',INV';
if sfSaveFieldKind in sfFieldKind then
s:=s+','+FieldKindNames[ord(ADataSet.Fields[i].FieldKind)]
else
s:=s+','+FieldKindNames[0];
{$IFDEF LEVEL4}
s:=s+','+QuoteString(ADataSet.Fields[i].DefaultExpression,'"');
{$ENDIF}
{$IFDEF DOTNET}
s:=QuoteString(s,FCSVQuote)+#13+#10;
l:=length(s);
WorkStream.WriteBuffer(bytesOF(s), l);
{$ELSE}
s:=QuoteString(PChar(s),FCSVQuote)+#13+#10;
l:=length(s);
WorkStream.WriteBuffer(Pointer(s)^, l);
{$ENDIF}
end;
end;
{$IFNDEF CSV_FILE_1XX_COMPATIBILITY}
// Check if to write index definitions.
if sfSaveIndexDef in sfIndexDef then
begin
// Write header.
{$IFDEF DOTNET}
s:=QuoteString(kbmIndexDefMagicStart,FCSVQuote)+#13+#10;
l:=length(s);
WorkStream.WriteBuffer(bytesOf(s),l);
{$ELSE}
s:=QuoteString(PChar(kbmIndexDefMagicStart),FCSVQuote)+#13+#10;
l:=length(s);
WorkStream.WriteBuffer(Pointer(s)^, l);
{$ENDIF}
// Write indexdefinitions.
for i:=0 to ADataSet.IndexDefs.count-1 do
with ADataSet.IndexDefs.Items[i] do
begin
s:=Name+'='+
QuoteString(Fields,FCSVQuote)+','+
{$IFDEF LEVEL3}
Name;
{$ELSE}
QuoteString(DisplayName,FCSVQuote);
{$ENDIF}
if ixDescending in Options then s:=s+',DESC';
if ixCaseInsensitive in Options then s:=s+',CASE';
{$IFNDEF LEVEL3}
if ixNonMaintained in Options then s:=s+',NONMT';
{$ENDIF}
if ixUnique in Options then s:=s+',UNIQ';
{$IFDEF DOTNET}
s:=QuoteString(s,FCSVQuote)+#13+#10;
l:=length(s);
WorkStream.WriteBuffer(bytesOf(s),l);
{$ELSE}
s:=QuoteString(PChar(s),FCSVQuote)+#13+#10;
l:=length(s);
WorkStream.WriteBuffer(Pointer(s)^,l);
{$ENDIF}
end;
// Write footer.
{$IFDEF DOTNET}
s:=QuoteString(kbmIndexDefMagicEnd,FCSVQuote)+#13+#10;
l:=length(s);
WorkStream.WriteBuffer(BytesOf(s),l);
{$ELSE}
s:=QuoteString(PChar(kbmIndexDefMagicEnd),FCSVQuote)+#13+#10;
l:=length(s);
WorkStream.WriteBuffer(Pointer(s)^,l);
{$ENDIF}
end;
{$ENDIF}
// Write footer.
{$IFDEF DOTNET}
s:=QuoteString(kbmTableDefMagicEnd,FCSVQuote)+#13+#10;
l:=length(s);
WorkStream.WriteBuffer(BytesOf(s), l);
{$ELSE}
s:=QuoteString(PChar(kbmTableDefMagicEnd),FCSVQuote)+#13+#10;
l:=length(s);
WorkStream.WriteBuffer(Pointer(s)^, l);
{$ENDIF}
end;
procedure TkbmCustomCSVStreamFormat.SaveData(ADataset:TkbmCustomMemTable);
var
i,j,l:integer;
nf:integer;
s,s1,a:string;
Accept:boolean;
null:boolean;
begin
ADataSet.SaveCount := 0;
// Setup flags for fields to save.
nf:=ADataSet.Fieldcount;
// Save header.
if not (sfSaveNoHeader in sfNoHeader) then
begin
// Write all field display names in CSV format.
s:='';
a:='';
for i:=0 to nf-1 do
begin
if (SaveFields[i]>=0) or (sfSavePlaceHolders in sfPlaceHolders) then
begin
{$IFDEF DOTNET}
s:=s+a+QuoteString(ADataSet.Fields[i].DisplayName,FCSVQuote);
{$ELSE}
s:=s+a+QuoteString(PChar(ADataSet.Fields[i].DisplayName),FCSVQuote);
{$ENDIF}
a:=FCSVFieldDelimiter;
end;
end;
if FCSVRecordDelimiter <> #0 then s:=s+FCSVRecordDelimiter;
s:=s+#13+#10;
l:=length(s);
{$IFDEF DOTNET}
WorkStream.Write(bytesOf(s), l);
{$ELSE}
WorkStream.Write(Pointer(s)^, l);
{$ENDIF}
end;
// Write all records in CSV format ordered by current index.
if sfSaveData in sfData then
begin
try
ADataSet.SavedCompletely := True;
for j:=0 to ADataSet.CurIndex.References.Count-1 do
begin
// Check if to save more.
if (ADataSet.SaveLimit>0) and (ADataSet.SaveCount>=ADataSet.SaveLimit) then
begin
ADataSet.SavedCompletely:=false;
break;
end;
// Check if to invoke progress event if any.
if (j mod 100)=0 then ADataSet.Progress(trunc((j/ADataSet.CurIndex.References.count)*100),mtpcSave);
// Setup which record to work on.
ADataSet.OverrideActiveRecordBuffer:=PkbmRecord(ADataSet.CurIndex.References.Items[j]);
if ADataSet.OverrideActiveRecordBuffer=nil then continue;
// Calculate fields.
ADataSet.__ClearCalcFields({$IFNDEF DOTNET}PChar(ADataSet.OverrideActiveRecordBuffer){$ELSE}ADataSet.OverrideActiveRecordBuffer{$ENDIF});
ADataSet.__GetCalcFields({$IFNDEF DOTNET}PChar(ADataSet.OverrideActiveRecordBuffer){$ELSE}ADataSet.OverrideActiveRecordBuffer{$ENDIF});
// Check filter of record.
Accept:=ADataSet.FilterRecord(ADataSet.OverrideActiveRecordBuffer,false);
if not Accept then continue;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -