Subversion Repositories delphiutils

Rev

Rev 96 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
96 daniel-mar 1
unit hl.Utils.AsciiTable;
89 daniel-mar 2
 
94 daniel-mar 3
// Download:
4
// https://github.com/danielmarschall/delphiutils/blob/master/Units/AsciiTable.pas
5
 
89 daniel-mar 6
(*
7
 * ASCII Table and CSV Generator Delphi Unit
97 daniel-mar 8
 * Revision 2024-02-08
89 daniel-mar 9
 *
96 daniel-mar 10
 * (C) 2022-2024 Daniel Marschall, HickelSOFT, ViaThinkSoft
89 daniel-mar 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);
94 daniel-mar 27
  try
28
    VirtTable.Clear;
89 daniel-mar 29
 
94 daniel-mar 30
    // Create Test data
31
    objLine := TVtsAsciiTableLine.Create;
32
    objLine.SetVal(0, 'Fruit', taCenter);
33
    objLine.SetVal(1, 'Amount', taCenter);
34
    VirtTable.Add(objLine);
89 daniel-mar 35
 
94 daniel-mar 36
    VirtTable.AddSeparator;
89 daniel-mar 37
 
94 daniel-mar 38
    objLine := TVtsAsciiTableLine.Create;
39
    objLine.SetVal(0, 'Apple', taLeftJustify);
40
    objLine.SetVal(1, '123', taRightJustify);
41
    VirtTable.Add(objLine);
89 daniel-mar 42
 
94 daniel-mar 43
    objLine := TVtsAsciiTableLine.Create;
44
    objLine.SetVal(0, 'Kiwi', taLeftJustify);
45
    objLine.SetVal(1, '1', taRightJustify);
46
    VirtTable.Add(objLine);
89 daniel-mar 47
 
94 daniel-mar 48
    objLine := TVtsAsciiTableLine.Create;
49
    objLine.SetVal(0, 'Asparagus (green)', taLeftJustify);
50
    objLine.SetVal(1, '9999', taRightJustify);
51
    VirtTable.Add(objLine);
89 daniel-mar 52
 
94 daniel-mar 53
    objLine := TVtsAsciiTableLine.Create;
54
    objLine.SetVal(0, 'Asparagus (white)', taLeftJustify);
55
    objLine.SetVal(1, '999', taRightJustify);
56
    VirtTable.Add(objLine);
89 daniel-mar 57
 
94 daniel-mar 58
    VirtTable.AddSeparator;
59
    VirtTable.AddSumLine;
91 daniel-mar 60
 
94 daniel-mar 61
    // Create ASCII table
62
    Memo1.Clear;
63
    VirtTable.GetASCIITable(Memo1.Lines);
89 daniel-mar 64
 
94 daniel-mar 65
    // Save ASCII table
66
    VirtTable.SaveASCIITable('Order.txt');
90 daniel-mar 67
 
94 daniel-mar 68
    // Create CSV
69
    Memo2.Clear;
70
    VirtTable.GetCSV(Memo2.Lines);
89 daniel-mar 71
 
94 daniel-mar 72
    // Save CSV
73
    VirtTable.SaveCSV('Order.csv');
74
  finally
75
    FreeAndNil(VirtTable);
76
  end;
89 daniel-mar 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 daniel-mar 91
  private
92
    IsSumLine: boolean;
93
    //IsSeparator: boolean;
89 daniel-mar 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;
92 daniel-mar 98
    DoSum: array[0..VTS_ASCII_TABLE_COLS-1] of boolean;
89 daniel-mar 99
    procedure Clear;
92 daniel-mar 100
    procedure SetVal(index: integer; ACont: string; AAlign: TAlignment=taLeftJustify;
101
      APadChar: char=' '; ADoSum: boolean=false);
89 daniel-mar 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;
94 daniel-mar 107
    Sum: array[0..VTS_ASCII_TABLE_COLS-1] of extended;
89 daniel-mar 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;
93 daniel-mar 116
    procedure GetASCIITable(sl: TStrings; spaceBetween: integer=3); overload;
117
    function GetASCIITable(spaceBetween: integer=3): string; overload;
90 daniel-mar 118
    procedure SaveASCIITable(filename: string; spaceBetween: integer=3);
89 daniel-mar 119
    procedure GetCSV(sl: TStrings);
90 daniel-mar 120
    procedure SaveCSV(filename: string);
89 daniel-mar 121
 
122
    procedure AddSeparator;
91 daniel-mar 123
    procedure AddSumLine;
89 daniel-mar 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
 
94 daniel-mar 133
uses
134
  Math;
135
 
89 daniel-mar 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
 
91 daniel-mar 148
procedure TVtsAsciiTable.AddSumLine;
149
var
150
  objLine: TVtsAsciiTableLine;
151
  j: Integer;
152
  analysis: TVtsAsciiTableAnalysis;
92 daniel-mar 153
  found: boolean;
91 daniel-mar 154
begin
155
  objLine := TVtsAsciiTableLine.Create;
156
  objLine.IsSumLine := true;
157
  analysis := GetAnalysis;
92 daniel-mar 158
  found := false;
91 daniel-mar 159
  for j := 0 to VTS_ASCII_TABLE_COLS-1 do
160
  begin
161
    if analysis.Sum[j] <> 0 then
92 daniel-mar 162
    begin
96 daniel-mar 163
      objLine.SetVal(j, FloatToStr(RoundTo(analysis.Sum[j], -2)), taRightJustify, ' ');
92 daniel-mar 164
      found := true;
165
    end;
91 daniel-mar 166
  end;
92 daniel-mar 167
  if found then
168
    Inherited Add(objLine)
169
  else
94 daniel-mar 170
    FreeAndNil(objLine);
91 daniel-mar 171
end;
172
 
89 daniel-mar 173
function TVtsAsciiTable.GetAnalysis: TVtsAsciiTableAnalysis;
174
var
175
  j: Integer;
176
  i: Integer;
177
  objLine: TVtsAsciiTableLine;
178
  len: Integer;
94 daniel-mar 179
  itmp: extended;
89 daniel-mar 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;
91 daniel-mar 185
    result.Sum[j] := 0;
89 daniel-mar 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]);
96 daniel-mar 195
        if objLine.DoSum[j] and TryStrToFloat(objLine.Cont[j], itmp) then
196
          result.Sum[j] := RoundTo(result.Sum[j] + itmp, -2);
89 daniel-mar 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
 
93 daniel-mar 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
 
89 daniel-mar 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;
97 daniel-mar 289
    sl.Add(TrimRight(sLine));
89 daniel-mar 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;
91 daniel-mar 322
    if objLine.IsSumLine then continue;
89 daniel-mar 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;
97 daniel-mar 334
    sl.Add(TrimRight(sLine));
89 daniel-mar 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
 
90 daniel-mar 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
 
89 daniel-mar 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;
92 daniel-mar 395
  AAlign: TAlignment=taLeftJustify; APadChar: char=' '; ADoSum: boolean=false);
89 daniel-mar 396
begin
397
  Self.Cont[index] := ACont;
398
  Self.Align[index] := AAlign;
399
  Self.PadChar[index] := APadChar;
92 daniel-mar 400
  Self.DoSum[index] := ADoSum;
89 daniel-mar 401
end;
402
 
97 daniel-mar 403
end.