123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597 |
- program dbftool;
- {
- Reads and exports DBF files.
- Can create a set of 2 demo DBF files in current directory to test with.
- Demonstrates creating DBF tables, filling it with data,
- and exporting datasets.
- }
- {$mode objfpc}{$H+}
- uses {$IFDEF UNIX} {$IFDEF UseCThreads}
- cthreads, {$ENDIF} {$ENDIF}
- Classes,
- SysUtils,
- CustApp,
- DB,
- dbf,
- dbf_fields,
- dbf_common,
- dateutils,
- fpdbexport,
- fpcsvexport,
- fpdbfexport,
- fpfixedexport,
- fprtfexport,
- fpsimplejsonexport,
- fpsimplexmlexport,
- fpsqlexport,
- fptexexport,
- fpxmlxsdexport;
- type
- { TDBFTool }
- TDBFTool = class(TCustomApplication)
- private
- // Exports recordset to specified format
- procedure ExportDBF(var MyDbf: TDbf; ExportFormat: string);
- // Executable name without path
- function GetExeName: string;
- protected
- procedure DoRun; override;
- public
- constructor Create(TheOwner: TComponent); override;
- destructor Destroy; override;
- procedure WriteHelp; virtual;
- end;
- // Creates 2 demonstration DBFs in Directory
- // with dbase compatibility level TableLevel
- procedure CreateDemoDBFs(Directory: string; TableLevel: integer);
- // Data structure and data adapted from Firebird employee sample database that
- // are also used in the SQLDB tutorials on Lazarus wiki/demo directory.
- var
- CurDir: string; //current directory
- NewDBF: TDBF;
- i: integer;
- begin
- // Get current working directory (need not be application directory):
- CurDir := '';
- GetDir(0,CurDir);
- NewDBF := TDBF.Create(nil);
- try
- if Directory = '' then
- begin
- NewDBF.FilePathFull := ExpandFileName(CurDir);
- end
- else
- NewDBF.FilePathFull := ExpandFileName(Directory) {full absolute path};
- if TableLevel <= 0 then
- NewDBF.TableLevel := 4 {default to DBase IV}
- else
- NewDBF.TableLevel := TableLevel;
- NewDBF.TableName := 'customer.dbf';
- writeln('Creating ', NewDBF.TableName, ' with table level ', NewDBF.TableLevel);
- if TableLevel >= 30 {Visual FoxPro} then
- begin
- NewDBF.FieldDefs.Add('CUST_NO', ftAutoInc);
- end
- else
- NewDBF.FieldDefs.Add('CUST_NO', ftInteger);
- NewDBF.FieldDefs.Add('CUSTOMER', ftString, 25);
- NewDBF.FieldDefs.Add('CITY', ftString, 25);
- NewDBF.FieldDefs.Add('COUNTRY', ftString, 15);
- NewDBF.CreateTable;
- NewDBF.Open;
- for i := 1 to 5 do //keep size manageable until we have working files
- begin
- NewDBF.Append;
- if (NewDBF.FieldDefs.Find('CUST_NO').DataType <> ftAutoInc) then
- NewDBF.FieldByName('CUST_NO').AsInteger := i;
- case i of
- 1:
- begin
- NewDBF.FieldByName('CUSTOMER').AsString := 'Michael Design';
- NewDBF.FieldByName('CITY').AsString := 'San Diego';
- NewDBF.FieldByName('COUNTRY').AsString := 'USA';
- end;
- 2: //Let's try a duplicate row
- begin
- NewDBF.FieldByName('CUSTOMER').AsString := 'Michael Design';
- NewDBF.FieldByName('CITY').AsString := 'San Diego';
- NewDBF.FieldByName('COUNTRY').AsString := 'USA';
- end;
- 3:
- begin
- NewDBF.FieldByName('CUSTOMER').AsString := 'VC Technologies';
- NewDBF.FieldByName('CITY').AsString := 'Dallas';
- NewDBF.FieldByName('COUNTRY').AsString := 'USA';
- end;
- 4:
- begin
- NewDBF.FieldByName('CUSTOMER').AsString := 'Klämpfl, Van Canneyt';
- NewDBF.FieldByName('CITY').AsString := 'Boston';
- NewDBF.FieldByName('COUNTRY').AsString := 'USA';
- end;
- 5:
- begin
- NewDBF.FieldByName('CUSTOMER').AsString := 'Felipe''s Bank';
- NewDBF.FieldByName('CITY').AsString := 'Manchester';
- NewDBF.FieldByName('COUNTRY').AsString := 'England';
- end;
- end;
- NewDBF.Post;
- end;
- NewDBF.Close;
- finally
- NewDBF.Free;
- end;
- NewDBF := TDBF.Create(nil);
- try
- if Directory = '' then
- NewDBF.FilePathFull := ExpandFileName(CurDir)
- else
- NewDBF.FilePathFull := ExpandFileName(Directory) {full absolute path};
- if TableLevel <= 0 then
- NewDBF.TableLevel := 4 {default to DBase IV}
- else
- NewDBF.TableLevel := TableLevel;
- NewDBF.TableName := 'employee.dbf';
- writeln('Creating ', NewDBF.TableName, ' with table level ', NewDBF.TableLevel);
- if TableLevel >= 30 {Visual FoxPro} then
- begin
- NewDBF.FieldDefs.Add('EMP_NO', ftAutoInc);
- end
- else
- NewDBF.FieldDefs.Add('EMP_NO', ftInteger);
- NewDBF.FieldDefs.Add('FIRST_NAME', ftString, 15);
- NewDBF.FieldDefs.Add('LAST_NAME', ftString, 20);
- NewDBF.FieldDefs.Add('PHONE_EXT', ftString, 4);
- NewDBF.FieldDefs.Add('JOB_CODE', ftString, 5);
- NewDBF.FieldDefs.Add('JOB_GRADE', ftInteger);
- NewDBF.FieldDefs.Add('JOB_COUNTR', ftString, 15); //Note 10 character limit for table/field names in most DBases
- NewDBF.FieldDefs.Add('SALARY', ftFloat);
- NewDBF.CreateTable;
- NewDBF.Open;
- for i := 1 to 5 do //keep size manageable until we have working files
- begin
- NewDBF.Append;
- if (NewDBF.FieldDefs.Find('EMP_NO').DataType <> ftAutoInc) then
- NewDBF.FieldByName('EMP_NO').AsInteger := i;
- case i of
- 1:
- begin
- NewDBF.FieldByName('FIRST_NAME').AsString := 'William';
- NewDBF.FieldByName('LAST_NAME').AsString := 'Shatner';
- NewDBF.FieldByName('PHONE_EXT').AsString := '1702';
- NewDBF.FieldByName('JOB_CODE').AsString := 'CEO';
- NewDBF.FieldByName('JOB_GRADE').AsInteger := 1;
- NewDBF.FieldByName('JOB_COUNTR').AsString := 'USA';
- NewDBF.FieldByName('SALARY').AsFloat := 48000;
- end;
- 2:
- begin
- NewDBF.FieldByName('FIRST_NAME').AsString := 'Ivan';
- NewDBF.FieldByName('LAST_NAME').AsString := 'Ishenin';
- NewDBF.FieldByName('PHONE_EXT').AsString := '9802';
- NewDBF.FieldByName('JOB_CODE').AsString := 'Eng';
- NewDBF.FieldByName('JOB_GRADE').AsInteger := 2;
- NewDBF.FieldByName('JOB_COUNTR').AsString := 'Russia';
- NewDBF.FieldByName('SALARY').AsFloat := 38000;
- end;
- 3:
- begin
- NewDBF.FieldByName('FIRST_NAME').AsString := 'Erin';
- NewDBF.FieldByName('LAST_NAME').AsString := 'Powell';
- NewDBF.FieldByName('PHONE_EXT').AsString := '1703';
- NewDBF.FieldByName('JOB_CODE').AsString := 'Admin';
- NewDBF.FieldByName('JOB_GRADE').AsInteger := 2;
- NewDBF.FieldByName('JOB_COUNTR').AsString := 'USA';
- NewDBF.FieldByName('SALARY').AsFloat := 45368;
- end;
- 4:
- begin
- NewDBF.FieldByName('FIRST_NAME').AsString := 'Margaret';
- NewDBF.FieldByName('LAST_NAME').AsString := 'Tetchy';
- NewDBF.FieldByName('PHONE_EXT').AsString := '3804';
- NewDBF.FieldByName('JOB_CODE').AsString := 'Eng';
- NewDBF.FieldByName('JOB_GRADE').AsInteger := 3;
- NewDBF.FieldByName('JOB_COUNTR').AsString := 'England';
- NewDBF.FieldByName('SALARY').AsFloat := 28045;
- end;
- 5:
- begin
- NewDBF.FieldByName('FIRST_NAME').AsString := 'Sergey';
- NewDBF.FieldByName('LAST_NAME').AsString := 'Bron';
- NewDBF.FieldByName('PHONE_EXT').AsString := '3807';
- NewDBF.FieldByName('JOB_CODE').AsString := 'Admin';
- NewDBF.FieldByName('JOB_GRADE').AsInteger := 3;
- NewDBF.FieldByName('JOB_COUNTR').AsString := 'England';
- NewDBF.FieldByName('SALARY').AsFloat := 24468;
- end;
- end;
- NewDBF.Post;
- end;
- NewDBF.Close;
- finally
- NewDBF.Free;
- end;
- end;
- // Gets list of all .dbf files in current directory and its subdirectories.
- procedure GetDBFList(Results: TStringList);
- var
- r: TSearchRec;
- begin
- results.Clear;
- if FindFirst('*', faAnyFile - faDirectory -
- {$WARNINGS OFF}
- faVolumeID - faSymLink
- {$WARNINGS ON}
- , r) = 0 then
- begin
- repeat
- begin
- // Cater for both case-sensitive and case-insensitive filesystems
- // ignore any directories
- if ((r.Attr and faDirectory) <> faDirectory) and
- (LowerCase(ExtractFileExt(r.Name))='.dbf') then
- results.add(expandfilename(r.Name));
- end;
- until (FindNext(r) <> 0);
- findclose(r);
- end;
- end;
- // Convert binary field contents to strings with hexadecimal representation.
- // Useful for displaying binary field contents.
- function BinFieldToHex(BinarySource: TField): string;
- var
- HexValue: PChar;
- begin
- Result := '';
- HexValue := StrAlloc(Length(BinarySource.AsBytes));
- try
- try
- BinToHex(PChar(BinarySource.AsBytes), HexValue, Length(BinarySource.AsBytes));
- Result := 'size: ' + IntToStr(Length(BinarySource.AsBytes)) + '; hex: ' + HexValue;
- except
- on E: Exception do
- begin
- Result := 'exception: ' + E.ClassName + '/' + E.Message;
- end;
- end;
- finally
- StrDispose(HexValue);
- end;
- end;
- // Writes contents of available records to screen
- procedure PrintRecords(DBf: TDBf);
- var
- i: integer;
- RecordCount: integer;
- begin
- Dbf.First;
- RecordCount:=0;
- while not (Dbf.EOF) do
- begin
- RecordCount := RecordCount + 1;
- writeln('Record ' + IntToStr(RecordCount));
- for i := 0 to DBf.Fields.Count - 1 do
- begin
- if DBF.fields[i].IsNull then
- writeln('Field ', DBf.Fields[i].FieldName, ' is ***NULL***')
- else
- if DBF.Fields[i].DataType in [ftVarBytes, ftBytes] then
- writeln('Field ', DBF.Fields[i].FieldName, ' has value: binary ' + BinFieldToHex(DBF.Fields[i]))
- else
- writeln('Field ', DBf.Fields[i].FieldName, ' has value: ' + DBf.fields[i].AsString);
- end;
- DBF.Next;
- writeln('');
- end;
- end;
- { TDBFTool }
- procedure TDBFTool.ExportDBF(var MyDbf: TDbf; ExportFormat: string);
- var
- ExportSettings: TCustomExportFormatSettings;
- Exporter: TCustomFileExporter;
- begin
- try
- case UpperCase(ExportFormat) of
- 'ACCESS', 'MSACCESS':
- begin
- Exporter := TXMLXSDExporter.Create(nil);
- ExportSettings := TXMLXSDFormatSettings.Create(true);
- (ExportSettings as TXMLXSDFormatSettings).CreateXSD := true;
- (ExportSettings as TXMLXSDFormatSettings).ExportFormat :=
- AccessCompatible;
- (ExportSettings as TXMLXSDFormatSettings).DecimalSeparator := '.';
- Exporter.FileName := MyDBF.FilePathFull + ChangeFileExt(MyDBF.TableName, '.xml');
- end;
- 'ADO', 'ADONET', 'ADO.NET':
- begin
- Exporter := TXMLXSDExporter.Create(nil);
- ExportSettings := TXMLXSDFormatSettings.Create(true);
- (ExportSettings as TXMLXSDFormatSettings).CreateXSD := true;
- (ExportSettings as TXMLXSDFormatSettings).ExportFormat :=
- ADONETCompatible;
- (ExportSettings as TXMLXSDFormatSettings).DecimalSeparator := '.';
- Exporter.FileName := MyDBF.FilePathFull + ChangeFileExt(MyDBF.TableName, '.xml');
- end;
- 'CSVEXCEL', 'EXCELCSV', 'CREATIVYST':
- begin
- Exporter := TCSVExporter.Create(nil);
- ExportSettings := TCSVFormatSettings.Create(true);
- (ExportSettings as TCSVFormatSettings).RowDelimiter:=LineEnding;
- //todo: delimiter?
- Exporter.FileName := MyDBF.FilePathFull + ChangeFileExt(MyDBF.TableName, '.csv');
- end;
- 'CSV', 'CSVRFC4180', 'CSVLIBRE', 'CSVLIBREOFFICE', 'CSVOPENOFFICE':
- begin
- Exporter := TCSVExporter.Create(nil);
- ExportSettings := TCSVFormatSettings.Create(true);
- (ExportSettings as TCSVFormatSettings).DecimalSeparator := '.';
- (ExportSettings as TCSVFormatSettings).StringQuoteChar := '"';
- Exporter.FileName := MyDBF.FilePathFull + ChangeFileExt(MyDBF.TableName, '.csv');
- end;
- 'DATASET', 'DELPHI':
- begin
- Exporter := TXMLXSDExporter.Create(nil);
- ExportSettings := TXMLXSDFormatSettings.Create(true);
- (ExportSettings as TXMLXSDFormatSettings).ExportFormat :=
- DelphiClientDataset;
- (ExportSettings as TXMLXSDFormatSettings).DecimalSeparator := '.';
- Exporter.FileName := MyDBF.FilePathFull + ChangeFileExt(MyDBF.TableName, '.xml');
- end;
- 'EXCEL', 'EXCELXML':
- begin
- Exporter := TXMLXSDExporter.Create(nil);
- ExportSettings := TXMLXSDFormatSettings.Create(true);
- (ExportSettings as TXMLXSDFormatSettings).ExportFormat := ExcelCompatible;
- (ExportSettings as TXMLXSDFormatSettings).DecimalSeparator := '.';
- Exporter.FileName := MyDBF.FilePathFull + ChangeFileExt(MyDBF.TableName, '.xml');
- end;
- 'JSON':
- begin
- Exporter := TSimpleJSONExporter.Create(nil);
- ExportSettings := TSimpleJSONFormatSettings.Create(true);
- Exporter.FileName := MyDBF.FilePathFull + ChangeFileExt(MyDBF.TableName, '.json');
- end;
- 'SIMPLEXML', 'XML':
- begin
- Exporter := TSimpleXMLExporter.Create(nil);
- ExportSettings := TSimpleXMLFormatSettings.Create(true);
- Exporter.FileName := MyDBF.FilePathFull + ChangeFileExt(MyDBF.TableName, '.xml');
- end;
- 'RTF':
- begin
- Exporter := TRTFExporter.Create(nil);
- ExportSettings := TSimpleXMLFormatSettings.Create(true);
- Exporter.FileName := MyDBF.FilePathFull + ChangeFileExt(MyDBF.TableName, '.rtf');
- end;
- 'SQL':
- begin
- Exporter := TSQLExporter.Create(nil);
- ExportSettings := TSQLFormatSettings.Create(true);
- (ExportSettings as TSQLFormatSettings).QuoteChar := '"';
- (ExportSettings as TSQLFormatSettings).DecimalSeparator := '.';
- (ExportSettings as TSQLFormatSettings).TableName := ChangeFileExt(MyDBF.TableName,'');
- (ExportSettings as TSQLFormatSettings).DateFormat := 'yyyy"-"mm"-"dd'; //ISO 8601, yyyy-mm-dd
- (ExportSettings as TSQLFormatSettings).TimeFormat := 'hh":"nn":"ss'; //ISO 8601, hh:mm:ss;
- (ExportSettings as TSQLFormatSettings).DateTimeFormat :=
- (ExportSettings as TSQLFormatSettings).DateFormat + '"T"' + (ExportSettings as TSQLFormatSettings).TimeFormat; //ISO 8601
- Exporter.FileName := MyDBF.FilePathFull + ChangeFileExt(MyDBF.TableName, '.sql');
- end;
- 'TEX', 'LATEX':
- begin
- Exporter := TTeXExporter.Create(nil);
- ExportSettings := TTeXExportFormatSettings.Create(true);
- Exporter.FileName := MyDBF.FilePathFull + ChangeFileExt(MyDBF.TableName, '.tex');
- end;
- 'TEXT', 'FIXED', 'FIXEDTEXT':
- begin
- Exporter := TFixedLengthExporter.Create(nil);
- ExportSettings := nil;
- Exporter.FileName := MyDBF.FilePathFull + ChangeFileExt(MyDBF.TableName, '.txt');
- end
- else
- begin
- writeln('***Error: Unknown export format ' + ExportFormat + ' specified' + '. Aborting');
- Exporter := nil;
- ExportSettings := nil;
- Terminate;
- Exit;
- end;
- end;
- if assigned(ExportSettings) then
- Exporter.FormatSettings := ExportSettings;
- Exporter.Dataset := MyDBF;
- MyDBF.First; // we've just read the last record - make sure export starts at beginning
- Exporter.Execute;
- writeln('Completed export to ' + Exporter.FileName);
- finally
- if assigned(Exporter) then
- Exporter.Free;
- if assigned(ExportSettings) then
- ExportSettings.Free;
- end;
- end;
- function TDBFTool.GetExeName: string;
- begin
- result := ExtractFileName(ExeName);
- end;
- procedure TDBFTool.DoRun;
- var
- DBFs: TStringList;
- Demo: boolean;
- ErrorMsg: string;
- FileNo: integer;
- MyDbf: TDbf;
- TableLevel: integer;
- begin
- // quick check parameters
- ErrorMsg := CheckOptions('h', 'createdemo exportformat: help tablelevel:');
- if ErrorMsg <> '' then
- begin
- ShowException(Exception.Create(ErrorMsg));
- Terminate;
- Exit;
- end;
- // parse parameters
- if HasOption('h', 'help') then
- begin
- WriteHelp;
- Terminate;
- Exit;
- end;
- DBFs := TStringList.Create;
- try
- Demo := false;
- if HasOption('createdemo') then
- Demo := true;
- TableLevel := 4; //DBF
- if HasOption('tablelevel') then
- TableLevel := StrToIntDef(GetOptionValue('tablelevel'), 4);
- if Demo then
- begin
- try
- CreateDemoDBFs('', TableLevel);
- except
- on E: Exception do
- begin
- writeln('*** Error creating demo databases: ' + E.Message);
- Terminate;
- Exit;
- end;
- end;
- end;
- // Process all dbfs if no files specified
- if DBFs.Count = 0 then
- GetDBFList(DBFs);
- if DBFs.Count = 0 then
- begin
- writeln('Could not find any dbf files.');
- writeln('Use ' + GetExeName + ' --createdemo to create some test DBF files.');
- end;
- for FileNo := 0 to DBFs.Count - 1 do
- begin
- if not (fileexists(DBFs[FileNo])) then
- begin
- writeln('Sorry, file ',DBFs[FileNo],' does not exist. Ignoring it.');
- continue;
- end;
- MyDbf := TDbf.Create(nil);
- try
- try
- MyDbf.FilePath := ExtractFilePath(DBFs[FileNo]);
- MyDbf.TableName := ExtractFileName(DBFs[FileNo]);
- MyDbf.ReadOnly := true;
- writeln('*** Opening: ' + DBFs[FileNo]);
- MyDbf.Open;
- writeln('Database tablelevel: ' + IntToStr(MyDbf.TableLevel));
- writeln('Database codepage: ' + IntToStr(MyDBF.CodePage));
- PrintRecords(MyDBF);
- if HasOption('exportformat') then
- begin
- try
- ExportDBF(MyDbf,GetOptionValue('exportformat'));
- except
- on E: Exception do
- begin
- writeln('*** Problem exporting file ', FileNo, ': ', E.Message);
- end;
- end;
- end;
- MyDbf.Close;
- except
- on E: Exception do
- begin
- writeln('*** Error reading file ', FileNo, ': ', E.Message);
- end;
- end;
- finally
- MyDbf.Free;
- end;
- end;
- finally
- DBFs.Free;
- end;
- // stop program loop
- Terminate;
- end;
- constructor TDBFTool.Create(TheOwner: TComponent);
- begin
- inherited Create(TheOwner);
- StopOnException := true;
- end;
- destructor TDBFTool.Destroy;
- begin
- inherited Destroy;
- end;
- procedure TDBFTool.WriteHelp;
- begin
- writeln('Read/print all dbfs in current directory');
- writeln('Usage info: ', GetExeName, ' -h');
- writeln('');
- writeln('--createdemo create demo database in current directory');
- writeln('--tablelevel=<n> optional: desired tablelevel for demo db');
- writeln(' 3 DBase III');
- writeln(' 4 DBase IV (default if no tablelevel given)');
- writeln(' 7 Visual DBase 7');
- writeln(' 25 FoxPro 2.x');
- writeln(' 30 Visual FoxPro');
- writeln('--exportformat=<text> export dbfs to format. Format can be:');
- writeln(' access Microsoft Access XML');
- writeln(' adonet ADO.Net dataset XML');
- writeln(' csvexcel Excel/Creativyst format CSV text file ');
- writeln(' (with locale dependent output)');
- writeln(' csvRFC4180 LibreOffice/RFC4180 format CSV text file');
- writeln(' dataset Delphi dataset XML');
- writeln(' excel Microsoft Excel XML');
- writeln(' fixedtext Fixed length text file');
- writeln(' json JSON file');
- writeln(' rtf Rich Text Format');
- writeln(' simplexml Simple XML');
- writeln(' sql SQL insert statements');
- writeln(' tex LaTeX file');
- end;
- var
- Application: TDBFTool;
- begin
- Application := TDBFTool.Create(nil);
- Application.Title := 'DBFTool';
- Application.Run;
- Application.Free;
- end.
|