Subversion Repositories delphiutils

Rev

Rev 93 | Rev 95 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. unit AsciiTable;
  2.  
  3. // Download:
  4. // https://github.com/danielmarschall/delphiutils/blob/master/Units/AsciiTable.pas
  5.  
  6. (*
  7.  * ASCII Table and CSV Generator Delphi Unit
  8.  * Revision 2023-12-08
  9.  *
  10.  * (C) 2022 Daniel Marschall, HickelSOFT, ViaThinkSoft
  11.  * Licensed under the terms of Apache 2.0
  12.  *)
  13.  
  14. {
  15.  
  16. Example usage:
  17.  
  18. uses
  19.   AsciiTable, ContNrs;
  20.  
  21. procedure TForm1.Button1Click(Sender: TObject);
  22. var
  23.   VirtTable: TVtsAsciiTable;
  24.   objLine: TVtsAsciiTableLine;
  25. begin
  26.   VirtTable := TVtsAsciiTable.Create(true);
  27.   try
  28.     VirtTable.Clear;
  29.  
  30.     // Create Test data
  31.     objLine := TVtsAsciiTableLine.Create;
  32.     objLine.SetVal(0, 'Fruit', taCenter);
  33.     objLine.SetVal(1, 'Amount', taCenter);
  34.     VirtTable.Add(objLine);
  35.  
  36.     VirtTable.AddSeparator;
  37.  
  38.     objLine := TVtsAsciiTableLine.Create;
  39.     objLine.SetVal(0, 'Apple', taLeftJustify);
  40.     objLine.SetVal(1, '123', taRightJustify);
  41.     VirtTable.Add(objLine);
  42.  
  43.     objLine := TVtsAsciiTableLine.Create;
  44.     objLine.SetVal(0, 'Kiwi', taLeftJustify);
  45.     objLine.SetVal(1, '1', taRightJustify);
  46.     VirtTable.Add(objLine);
  47.  
  48.     objLine := TVtsAsciiTableLine.Create;
  49.     objLine.SetVal(0, 'Asparagus (green)', taLeftJustify);
  50.     objLine.SetVal(1, '9999', taRightJustify);
  51.     VirtTable.Add(objLine);
  52.  
  53.     objLine := TVtsAsciiTableLine.Create;
  54.     objLine.SetVal(0, 'Asparagus (white)', taLeftJustify);
  55.     objLine.SetVal(1, '999', taRightJustify);
  56.     VirtTable.Add(objLine);
  57.  
  58.     VirtTable.AddSeparator;
  59.     VirtTable.AddSumLine;
  60.  
  61.     // Create ASCII table
  62.     Memo1.Clear;
  63.     VirtTable.GetASCIITable(Memo1.Lines);
  64.  
  65.     // Save ASCII table
  66.     VirtTable.SaveASCIITable('Order.txt');
  67.  
  68.     // Create CSV
  69.     Memo2.Clear;
  70.     VirtTable.GetCSV(Memo2.Lines);
  71.  
  72.     // Save CSV
  73.     VirtTable.SaveCSV('Order.csv');
  74.   finally
  75.     FreeAndNil(VirtTable);
  76.   end;
  77. end;
  78.  
  79. }
  80.  
  81. interface
  82.  
  83. uses
  84.   ContNrs, Classes, SysUtils;
  85.  
  86. const
  87.   VTS_ASCII_TABLE_COLS = 10;
  88.  
  89. type
  90.   TVtsAsciiTableLine = class(TObject)
  91.   private
  92.     IsSumLine: boolean;
  93.     //IsSeparator: boolean;
  94.   public
  95.     Cont: array[0..VTS_ASCII_TABLE_COLS-1] of string;
  96.     Align: array[0..VTS_ASCII_TABLE_COLS-1] of TAlignment;
  97.     PadChar: array[0..VTS_ASCII_TABLE_COLS-1] of char;
  98.     DoSum: array[0..VTS_ASCII_TABLE_COLS-1] of boolean;
  99.     procedure Clear;
  100.     procedure SetVal(index: integer; ACont: string; AAlign: TAlignment=taLeftJustify;
  101.       APadChar: char=' '; ADoSum: boolean=false);
  102.   end;
  103.  
  104.   TVtsAsciiTableAnalysis = record
  105.     MaxLen: array[0..VTS_ASCII_TABLE_COLS-1] of integer;
  106.     Used: array[0..VTS_ASCII_TABLE_COLS-1] of boolean;
  107.     Sum: array[0..VTS_ASCII_TABLE_COLS-1] of extended;
  108.   end;
  109.  
  110.   TVtsAsciiTable = class(TObjectList{<TVtsAsciiTableLine>})
  111.   private
  112.     function GetItem(Index: Integer): TVtsAsciiTableLine;
  113.     procedure SetItem(Index: Integer; const Value: TVtsAsciiTableLine);
  114.   public
  115.     function GetAnalysis: TVtsAsciiTableAnalysis;
  116.     procedure GetASCIITable(sl: TStrings; spaceBetween: integer=3); overload;
  117.     function GetASCIITable(spaceBetween: integer=3): string; overload;
  118.     procedure SaveASCIITable(filename: string; spaceBetween: integer=3);
  119.     procedure GetCSV(sl: TStrings);
  120.     procedure SaveCSV(filename: string);
  121.  
  122.     procedure AddSeparator;
  123.     procedure AddSumLine;
  124.  
  125.     // Just a little bit type-safe... The rest stays TObject for now
  126.     function Add(AObject: TVtsAsciiTableLine): Integer; reintroduce;
  127.     property Items[Index: Integer]: TVtsAsciiTableLine read GetItem write SetItem;
  128.     procedure Insert(Index: Integer; AObject: TVtsAsciiTableLine); reintroduce;
  129.   end;
  130.  
  131. implementation
  132.  
  133. uses
  134.   Math;
  135.  
  136. { TVtsAsciiTable }
  137.  
  138. function TVtsAsciiTable.Add(AObject: TVtsAsciiTableLine): Integer;
  139. begin
  140.   result := Inherited Add(AObject);
  141. end;
  142.  
  143. procedure TVtsAsciiTable.AddSeparator;
  144. begin
  145.   Inherited Add(nil);
  146. end;
  147.  
  148. procedure TVtsAsciiTable.AddSumLine;
  149. var
  150.   objLine: TVtsAsciiTableLine;
  151.   j: Integer;
  152.   analysis: TVtsAsciiTableAnalysis;
  153.   found: boolean;
  154. begin
  155.   objLine := TVtsAsciiTableLine.Create;
  156.   objLine.IsSumLine := true;
  157.   analysis := GetAnalysis;
  158.   found := false;
  159.   for j := 0 to VTS_ASCII_TABLE_COLS-1 do
  160.   begin
  161.     if analysis.Sum[j] <> 0 then
  162.     begin
  163.       objLine.SetVal(j, FloatToStr(RoundTo(analysis.Sum[j],2)), taRightJustify, ' ');
  164.       found := true;
  165.     end;
  166.   end;
  167.   if found then
  168.     Inherited Add(objLine)
  169.   else
  170.     FreeAndNil(objLine);
  171. end;
  172.  
  173. function TVtsAsciiTable.GetAnalysis: TVtsAsciiTableAnalysis;
  174. var
  175.   j: Integer;
  176.   i: Integer;
  177.   objLine: TVtsAsciiTableLine;
  178.   len: Integer;
  179.   itmp: extended;
  180. begin
  181.   for j := 0 to VTS_ASCII_TABLE_COLS-1 do
  182.   begin
  183.     result.MaxLen[j] := 0;
  184.     result.Used[j] := false;
  185.     result.Sum[j] := 0;
  186.   end;
  187.   for i := 0 to Self.Count-1 do
  188.   begin
  189.     objLine := Self.items[i] as TVtsAsciiTableLine;
  190.     if objLine <> nil then
  191.     begin
  192.       for j := 0 to VTS_ASCII_TABLE_COLS-1 do
  193.       begin
  194.         len := Length(objLine.Cont[j]);
  195.         if TryStrToFloat(RoundTo(objLine.Cont[j],2), itmp) and objLine.DoSum[j] then
  196.           result.Sum[j] := result.Sum[j] + itmp;
  197.         if len > result.MaxLen[j] then
  198.           result.MaxLen[j] := len;
  199.         if len > 0 then
  200.           result.Used[j] := true;
  201.       end;
  202.     end;
  203.   end;
  204. end;
  205.  
  206. function TVtsAsciiTable.GetASCIITable(spaceBetween: integer): string;
  207. var
  208.   sl: TStringList;
  209. begin
  210.   sl := TStringList.Create;
  211.   try
  212.     GetASCIITable(sl, spaceBetween);
  213.     result := sl.Text;
  214.   finally
  215.     FreeAndNil(sl);
  216.   end;
  217. end;
  218.  
  219. procedure TVtsAsciiTable.GetASCIITable(sl: TStrings; spaceBetween: integer=3);
  220. var
  221.   analysis: TVtsAsciiTableAnalysis;
  222.   objLine: TVtsAsciiTableLine;
  223.   i: Integer;
  224.   sLine: string;
  225.   j: Integer;
  226.   itmp: Integer;
  227.   padchar: Char;
  228.   firstcol: boolean;
  229.   width: Integer;
  230. begin
  231.   analysis := GetAnalysis;
  232.   //sl.Clear;
  233.   for i := 0 to Self.Count-1 do
  234.   begin
  235.     objLine := Self.items[i] as TVtsAsciiTableLine;
  236.     sLine := '';
  237.     if objLine <> nil then
  238.     begin
  239.       firstcol := true;
  240.       for j := 0 to VTS_ASCII_TABLE_COLS-1 do
  241.       begin
  242.         if not analysis.Used[j] then continue;
  243.  
  244.         padchar := objLine.PadChar[j];
  245.         if padchar = #0 then padchar := ' ';
  246.  
  247.         if firstcol then
  248.           firstcol := false
  249.         else
  250.           sLine := sLine + StringOfChar(' ', spaceBetween);
  251.  
  252.         if objLine.Align[j] = taRightJustify then
  253.         begin
  254.           sLine := sLine + StringOfChar(padchar, analysis.MaxLen[j]-Length(objLine.Cont[j]));
  255.           sLine := sLine + objLine.Cont[j];
  256.         end
  257.         else if objLine.Align[j] = taLeftJustify then
  258.         begin
  259.           sLine := sLine + objLine.Cont[j];
  260.           sLine := sLine + StringOfChar(padchar, analysis.MaxLen[j]-Length(objLine.Cont[j]));
  261.         end
  262.         else if objLine.Align[j] = taCenter then
  263.         begin
  264.           if Odd(analysis.MaxLen[j]-Length(objLine.Cont[j])) then itmp := 1 else itmp := 0;
  265.           sLine := sLine + StringOfChar(padchar, (analysis.MaxLen[j]-Length(objLine.Cont[j])) div 2);
  266.           sLine := sLine + objLine.Cont[j];
  267.           sLine := sLine + StringOfChar(padchar, (analysis.MaxLen[j]-Length(objLine.Cont[j])) div 2 + itmp);
  268.         end
  269.         else
  270.           Assert(false);
  271.       end;
  272.     end
  273.     else
  274.     begin
  275.       firstcol := true;
  276.       width := 0;
  277.       for j := 0 to VTS_ASCII_TABLE_COLS-1 do
  278.       begin
  279.         if not analysis.Used[j] then continue;
  280.         if firstcol then
  281.           firstcol := false
  282.         else
  283.           width := width + spaceBetween;
  284.         width := width + analysis.MaxLen[j];
  285.       end;
  286.  
  287.       sLine := sLine + StringOfChar('-', Width);
  288.     end;
  289.     sl.Add(sLine);
  290.   end;
  291. end;
  292.  
  293. function CsvQuoteStr(s: string): string;
  294. begin
  295.   s := StringReplace(s, #13#10, ' ', [rfReplaceAll]);
  296.   s := StringReplace(s, #13, ' ', [rfReplaceAll]);
  297.   s := StringReplace(s, #10, ' ', [rfReplaceAll]);
  298.   if s = '' then
  299.     result := ''
  300.   else if (AnsiPos('"', s)>0) or (AnsiPos('''', s)>0) or (AnsiPos(';', s)>0) or
  301.           (AnsiPos(#9, s)>0) or (AnsiPos(' ', s)>0) then
  302.     result := '"' + StringReplace(s, '"', '""', [rfReplaceAll]) + '"'
  303.   else
  304.     result := s;
  305. end;
  306.  
  307. procedure TVtsAsciiTable.GetCSV(sl: TStrings);
  308. var
  309.   analysis: TVtsAsciiTableAnalysis;
  310.   objLine: TVtsAsciiTableLine;
  311.   i: Integer;
  312.   sLine: string;
  313.   j: Integer;
  314.   firstcol: boolean;
  315. begin
  316.   analysis := GetAnalysis;
  317.   //sl.Clear;
  318.   for i := 0 to Self.Count-1 do
  319.   begin
  320.     objLine := Self.items[i] as TVtsAsciiTableLine;
  321.     if objLine = nil then continue;
  322.     if objLine.IsSumLine then continue;
  323.     sLine := '';
  324.     firstcol := true;
  325.     for j := 0 to VTS_ASCII_TABLE_COLS-1 do
  326.     begin
  327.       if not analysis.Used[j] then continue;
  328.       if firstcol then
  329.         firstcol := false
  330.       else
  331.         sLine := sLine + ';';
  332.       sLine := sLine + CsvQuoteStr(objLine.Cont[j]);
  333.     end;
  334.     sl.Add(sLine);
  335.   end;
  336. end;
  337.  
  338. function TVtsAsciiTable.GetItem(Index: Integer): TVtsAsciiTableLine;
  339. begin
  340.   result := (Inherited Items[Index]) as TVtsAsciiTableLine;
  341. end;
  342.  
  343. procedure TVtsAsciiTable.Insert(Index: Integer; AObject: TVtsAsciiTableLine);
  344. begin
  345.   Inherited Insert(Index, AObject);
  346. end;
  347.  
  348. procedure TVtsAsciiTable.SaveASCIITable(filename: string;
  349.   spaceBetween: integer);
  350. var
  351.   sl: TStringList;
  352. begin
  353.   sl := TStringList.Create;
  354.   try
  355.     GetASCIITable(sl, spaceBetween);
  356.     sl.SaveToFile(filename);
  357.   finally
  358.     FreeAndNil(sl);
  359.   end;
  360. end;
  361.  
  362. procedure TVtsAsciiTable.SaveCSV(filename: string);
  363. var
  364.   sl: TStringList;
  365. begin
  366.   sl := TStringList.Create;
  367.   try
  368.     GetCSV(sl);
  369.     sl.SaveToFile(filename);
  370.   finally
  371.     FreeAndNil(sl);
  372.   end;
  373. end;
  374.  
  375. procedure TVtsAsciiTable.SetItem(Index: Integer; const Value: TVtsAsciiTableLine);
  376. begin
  377.   Inherited Items[Index] := Value;
  378. end;
  379.  
  380. { TVtsAsciiTableLine }
  381.  
  382. procedure TVtsAsciiTableLine.Clear;
  383. var
  384.   i: Integer;
  385. begin
  386.   for i := 0 to VTS_ASCII_TABLE_COLS-1 do
  387.   begin
  388.     PadChar[i] := #0;
  389.     Align[i] := taLeftJustify;
  390.     Cont[i] := '';
  391.   end;
  392. end;
  393.  
  394. procedure TVtsAsciiTableLine.SetVal(index: integer; ACont: string;
  395.   AAlign: TAlignment=taLeftJustify; APadChar: char=' '; ADoSum: boolean=false);
  396. begin
  397.   Self.Cont[index] := ACont;
  398.   Self.Align[index] := AAlign;
  399.   Self.PadChar[index] := APadChar;
  400.   Self.DoSum[index] := ADoSum;
  401. end;
  402.  
  403. end.