Recent

Author Topic: Little bit...  (Read 2722 times)

egsuh

  • Hero Member
  • *****
  • Posts: 1790
Little bit...
« on: April 10, 2026, 01:14:38 pm »
Please look at the attached code.

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var
  3.    tl : TStringList;
  4.    ti: integer;
  5. begin
  6.    tl := TStringList.Create;
  7.    tl.StrictDelimiter:= true;
  8.    tl.QuoteChar:= '"';
  9.    tl.AlwaysQuote:= false;
  10.  
  11.    memo1.Clear;
  12.  
  13.    tl.CommaText := '1=line1'#13'comment1,2=line2, "3=l,ine3"';
  14.    memo1.lines.add('count=%d', [tl.count]);  // count = 4
  15.    for ti := 0 to tl.count-1 do memo1.lines.Add(tl[ti]);
  16.  
  17.    
  18.    memo1.lines.add('');
  19.    tl.CommaText := '1=line1'#13'comment1,2=line2,"3=l,ine3"';
  20.    memo1.lines.add('count=%d', [tl.count]); // count = 3
  21.    for ti := 0 to tl.count-1 do memo1.lines.Add(tl[ti]);
  22.  
  23.    memo1.lines.add('');
  24.    tl.CommaText := '1=line1'#13'comment1,2=line\"2,"3=line,3"';
  25.    memo1.lines.add('count=%d', [tl.count]); // count = 3
  26.    for ti := 0 to tl.count-1 do memo1.lines.Add(tl[ti]);
  27.  
  28.    tl.free;
  29. end;
  30.  

The first string and the second line has only one difference: existence of blank between comma(,) and double quolte("). But they have different results.

Third string is to check working of escape char(\).

Can we say this is a bug?

tetrastes

  • Hero Member
  • *****
  • Posts: 762
Re: Little bit...
« Reply #1 on: April 10, 2026, 03:42:25 pm »
No.

jamie

  • Hero Member
  • *****
  • Posts: 7701
Re: Little bit...
« Reply #2 on: April 10, 2026, 03:42:58 pm »
Nope, not a bug but your code with the use of #13.

Jamie
The only true wisdom is knowing you know nothing

n7800

  • Hero Member
  • *****
  • Posts: 689
  • Lazarus IDE contributor
    • GitLab profile
Re: Little bit...
« Reply #3 on: April 11, 2026, 03:30:57 am »
No.

Your detailed response was very helpful to the author... But this is an appropriate answer, given the topic title ))

I hope the author comes up with a more descriptive name next time.

Nope, not a bug but your code with the use of #13.

The #13 has nothing to do with it, the behavior is the same without it.

However, using the cross-platform constant "LineEnding" instead of the characters "#10" and "#13" is a good tip ))

The first string and the second line has only one difference: existence of blank between comma(,) and double quolte("). But they have different results.

This is due to the "StrictDelimiter" property. Change it to "false" and the resulting strings will be the same.

Because of this property, a whitespace character after a delimiter is considered part of the string, not a delimiter. Since a whitespace character isn't a quotation mark, the following text is considered unquoted (it simply contains quotation marks) and is read until the next delimiter.

Third string is to check working of escape char(\).

The documentation only briefly mentions that need to double quotation marks to escape them. But it doesn't specify that if the text isn't in quotation marks, you don't need to escape them - the text will be read until the next delimiter.

That's why your example in my case shows the original text as '2=line\"2'. I don't know why you're seeing some strange character instead of a slash, it might be localization issues...
« Last Edit: April 11, 2026, 03:33:37 am by n7800 »

n7800

  • Hero Member
  • *****
  • Posts: 689
  • Lazarus IDE contributor
    • GitLab profile
Re: Little bit...
« Reply #4 on: April 11, 2026, 03:36:44 am »
As far as I know, all of this is compatible with Delphi. There's a issue with a similar problem, and I'm glad it was closed without a fix. Trying to improve it with additional options will only make things more complicated.

n7800

  • Hero Member
  • *****
  • Posts: 689
  • Lazarus IDE contributor
    • GitLab profile
Re: Little bit...
« Reply #5 on: April 11, 2026, 03:38:31 am »
I have already suggested some improvements to the documentation, I will add more later.

n7800

  • Hero Member
  • *****
  • Posts: 689
  • Lazarus IDE contributor
    • GitLab profile
Re: Little bit...
« Reply #6 on: April 11, 2026, 03:51:08 am »
Code: Pascal  [Select][+][-]
  1. ...
  2.    tl.StrictDelimiter:= true;
  3.    tl.QuoteChar:= '"';
  4.    tl.AlwaysQuote:= false;
  5. ...
  6.  

By the way, you specified "QuoteChar", but it's not used in "CommaText". You'll need "DelimitedText" if you want to use quotes other than double quotes (this is documented).

"AlwaysQuote" is also unnecessary in this case, as it's only used when reading this property, not writing it.

egsuh

  • Hero Member
  • *****
  • Posts: 1790
Re: Little bit...
« Reply #7 on: April 12, 2026, 11:16:01 am »
Sorry for not specifying the subject in more details. Actually I'm thinking of a scheme that moves database record to TStringList type, like

Code: Pascal  [Select][+][-]
  1.     for Afield in DataSet do AStringList.AddPair(Afield.FieldName, Afield.AsString);
  2.  
  3. // And reversly,
  4.  
  5.     for i := 0 to AStringList.Count - 1 do
  6.            DataSet.FieldByName(AstringList.Names[i]).AsString :=  AStringList.ValueFromIndex[i];
  7.  


I've read on MORMOT, etc. but still think they are too heavy for my application.

And some fields are text blob types. The content of the blob field may contain line breaks, quotes, double quotes, commas, and other white space characters. So I'm looking for some reliable method. I tested some methods. First one is using TStream, but this failed.

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button2Click(Sender: TObject);
  2. var
  3.    tl1, tl2: TStringList;
  4.    mem1: TMemoryStream;
  5. begin
  6.    tl1 := TStringList.Create;
  7.    tl2 := TStringList.Create;
  8.  
  9.    // tl1.LineBreak := #36;
  10.    // tl2.LineBreak := #36;
  11.  
  12.    with tl1 do begin
  13.       Add ('line 1' + LineEnding + ' this is line 1');
  14.       Add ('line 2' + LineEnding + ' this is line 2');
  15.  
  16.       Memo1.Lines.Add(IntToStr(Count));  // 2
  17.    end;
  18.  
  19.    mem1 := TMemoryStream.Create;
  20.    tl1.SaveToStream(mem1);
  21.  
  22.    mem1.Position:= 0;
  23.    tl2.LoadFromStream(mem1);
  24.  
  25.    Memo1.Lines.Add(IntToStr(tl2.Count));  // 4
  26.  
  27.    tl1.Free;
  28.    tl2.Free;
  29.    mem1.Free;
  30. end;

They gave different results.

So, I uncommonted foillowing lines,

     tl1.LineBreak := #36;
     tl2.LineBreak := #36;


And then the results were correct.   #36 is simple randomly selected.

I think this is the SIMPLEST and MOST RELIABLE method for my purpose, even when using syntax like tl2.text := ts1.text , as well as via TStreams.

What do you think? 

And I remember there was a Lazarus wiki page that reviewed which characters are appropriate for these purposes  (instead of #36). Please advise me where I can find it. 

jamie

  • Hero Member
  • *****
  • Posts: 7701
Re: Little bit...
« Reply #8 on: April 12, 2026, 01:04:11 pm »
If u need control chars in yours string in objects get confused with them then you could try json string fron the json unit.
The only true wisdom is knowing you know nothing

egsuh

  • Hero Member
  • *****
  • Posts: 1790
Re: Little bit...
« Reply #9 on: April 13, 2026, 09:37:52 am »
I found there are control characters for US(#31), FS(#30), GS(#29), and FS(#28). Trying and testing #31 for TStrings.LineBreak.

jamie

  • Hero Member
  • *****
  • Posts: 7701
Re: Little bit...
« Reply #10 on: April 13, 2026, 12:17:43 pm »
StringLists have object pointers for each entry so in theory you can allocate a buffer of blob data for each entry with a human readable entry.

stringlist.addooject(test,pointer)

many of the controls that list strings have a object pointer for each entry.

The only true wisdom is knowing you know nothing

ASerge

  • Hero Member
  • *****
  • Posts: 2492
Re: Little bit...
« Reply #11 on: April 18, 2026, 12:48:46 am »
I think it will work:
Code: Pascal  [Select][+][-]
  1. var
  2.   L: TStringList;
  3.   i: SizeInt;
  4. begin
  5.   L := TStringList.Create;
  6.   try
  7.     L.StrictDelimiter := True;
  8.     L.Delimiter := ',';
  9.     L.QuoteChar := #0;
  10.     Memo1.Clear;
  11.     L.DelimitedText := '1=line1 comment1,2=line2, "3=l,ine3"';
  12.     Memo1.Append(Format('count=%d', [L.Count]));  // count = 4
  13.     for i := 0 to L.Count - 1 do
  14.       Memo1.Append(L[i]);
  15.     Memo1.Append('');
  16.     L.DelimitedText := '1=line1 comment1,2=line2,"3=l,ine3"';
  17.     Memo1.Append(Format('count=%d', [L.Count]));  // count = 4
  18.     for i := 0 to L.Count - 1 do
  19.       Memo1.Append(L[i]);
  20.     Memo1.Append('');
  21.     L.DelimitedText := '1=line1 comment1,2=line\"2,"3=line,3"';
  22.     Memo1.Append(Format('count=%d', [L.Count]));  // count = 4
  23.     for i := 0 to L.Count - 1 do
  24.       Memo1.Append(L[i]);
  25.   finally
  26.     L.Free;
  27.   end;
  28. end;

Thaddy

  • Hero Member
  • *****
  • Posts: 19129
  • Glad to be alive.
Re: Little bit...
« Reply #12 on: April 18, 2026, 10:08:55 pm »
[editted]
You can simply add a formatstring property to TStringlist.
Code: Pascal  [Select][+][-]
  1. {$mode objfpc}{$H+}
  2. uses sysutils,classes;
  3.  
  4. type
  5.   TFmtStringlist = Class(TStringlist)
  6.   strict private
  7.     FmtStr:String;
  8.   public
  9.     procedure Add(const Args:array of Const);overload;
  10.   published
  11.     property FormatString:String read FmtStr write FmtStr;
  12.   end;
  13.  
  14. procedure TFmtStringlist.Add(Const Args:array of const);
  15. begin
  16.   // inherited Add(Format(FmtStr,Args));
  17.   // Freepascal supports:
  18.   inherited add(FmtStr,Args);
  19. end;
  20.  
  21. var
  22.   List:TFmtStringlist;
  23. begin
  24.   List := TFmtStringlist.create;
  25.   try
  26.     { Set the formatstring }
  27.     List.FormatString := 'Name: %10s|'#13#10' Age: %10d|';
  28.     { You can subsequently leave out the format string }
  29.     List.Add(['Tom',48]);
  30.     List.Add(['Maybelline',68]);
  31.   finally
  32.     Writeln(List.text);
  33.     List.free;
  34.    end;
  35. end.
All other suggestions may still apply.
The above code is a reworking of some code I saw many years back, so you may also have seen it before.
(Only change is basically Add(Format(Fmt,Args)) dropped for Add(Fmt,Args);
« Last Edit: April 18, 2026, 10:13:06 pm by Thaddy »
objects are fine constructs. You can even initialize them with constructors.

mas steindorff

  • Hero Member
  • *****
  • Posts: 576
Re: Little bit...
« Reply #13 on: April 19, 2026, 12:55:42 am »
The above code is a reworking of some code I saw many years back, so you may also have seen it before.
(Only change is basically Add(Format(Fmt,Args)) dropped for Add(Fmt,Args);
Just looking for some more info...
1: Are you saying .Add with 2 parameters will automatically use the 1st as a format string?  (The TStringList document doesn't say anything about this)

2: I can say I've ever passed a string to a procedure in an "array of const". Is this something newly added after FPC 3.2 or have I missed out in my studies? 
MAS
windows 10 &11, Ubuntu 21+ IDE 3.4 general releases

jamie

  • Hero Member
  • *****
  • Posts: 7701
Re: Little bit...
« Reply #14 on: April 19, 2026, 01:54:14 am »
Array of constants have been part of the language as far as I can remember, however, Tstringlist does not have that, it's simply added for an example.

You can, however, make a custom input because for each element of the array, there is type information that allows you can examine and treat it like a random input scenario.

 It's like working with an array of variants.

Jamie
The only true wisdom is knowing you know nothing

 

TinyPortal © 2005-2018