Delphi: StringList Delimiter is always a space character even if Delimiter is set
Asked Answered
P

6

62

I am having trouble with the delimiter in the TStringList Class. Take a look:

var
  s: string;
  sl: TStringList;

begin
  sl := TStringList.Create;
  s := 'Users^foo bar^bar foo^foobar^barfoo';
  sl.Delimiter := '^';
  sl.DelimitedText := s;
  ShowMessage(sl[1]);
end;

sl[1] SHOULD return 'foo bar'

sl[1] DOES return 'foo'

It seems that the delimiter is now '^' AND ' '

Any ideas?

Pentathlon answered 26/8, 2009 at 14:16 Comment(3)
What version of Delphi are you working in?Durer
possible duplicate of Split a string into an array of strings based on a delimiterTaligrade
I've never been more angry with a language bug than I am right now. Thank you, Acron, for bringing this to my attention.Pullover
D
96

You should set s1.StrictDelimiter := True for spaces not to be considered delimiters, more info here.

Since you work in a version that does not support the above (as was clarified after the answer was submitted), you have two options:

  1. Find a character you know will not be used in the original text (e.g. underscore), convert all spaces to that character before splitting, and convert back after splitting. This is robosoft's suggestion.
  2. If you don't have inverted commas and spaces in the text, you can use Alexander's trick and wrap the text between delimiters in inverted command, so that 'hello hello^bye bye' turns to '"hello hello"^"bye bye"'. If you do choose this path and it works for you, please accept Alexander's answer and not mine, he also provides the code to implement it.

Both workarounds not using StrictDelimiter have limitations: the first requires some unused character, and the second requires no inverted commas and spaces in the original text.

Maybe it's time to upgrade to a newer version of Delphi :)

Durer answered 26/8, 2009 at 14:25 Comment(4)
What do you want to work around? Looks like a solution to me.Audette
Your starting point would be to read the source code for SetDelimitedText in Classes.pas, and either subclass TStrings to create a version that handles this the way you need, or just write a stand-alone function to split the string on carets and return a string list.Colotomy
Could you temporarily turn spaces into something else, then change them back afterwards? Any other character that wouldn't normally appear in your list would work. It's a hack, or as we prefer 'necessary evil as a workaround'. :-)Countermove
@Colotomy - That was the first significant sub-class I ever wrote in Delphi 3!Tampere
M
19
sl.DelimitedText := '"' + StringReplace(S, sl.Delimiter, '"' + sl.Delimiter + '"', [rfReplaceAll]) + '"';
Misdoing answered 26/8, 2009 at 15:56 Comment(5)
Crap, you beat me, I was typing exactly the same answer :)Faustino
Very clever, thank you Alexander, aswell as The_Fox, even if you diddn't help me directly, but you intended to do so :)Pentathlon
+1 Very nice, but has a problem if the original string contains a subsection like ' " " ' (i.e. inverted commas and spaces in the text)Durer
>>> has a problem if the original string contains a subsection like ' " " ' And how can user name contain '"' char? :)Misdoing
@Alexander - Here's an example: 'Bill "The Womanizer" Clinton' :)Durer
E
6

Ryan has provider an excellent solution to this problem using ExtractStrings() function in Delphi. See:

Parsing a string using a delimiter to a TStringList, seems to also parse on spaces (Delphi)

So in your case, replace calls to sl.Delimiter and sl.DelimitedText with the line below: ExtractStrings(['^'], [], PChar(S), sl);

Erinnerinna answered 5/3, 2013 at 13:45 Comment(0)
F
4

Work's in Delphi 7 "like gloves" for me. This is my function after apply Alexander's Trick:

procedure Split (const Delimiter: Char; Input: string; const Strings: TStrings) ;
begin
   Assert(Assigned(Strings)) ;
   Strings.Clear;
   Strings.Delimiter := Delimiter;
   Strings.DelimitedText :=  '"' + StringReplace(Input, Delimiter, '"' + Delimiter + '"', [rfReplaceAll]) + '"' ;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Edit1.Text := 'Users^foo bar^bar foo^foobar^barfoo';
  Split('^',Edit1.Text,Memo1.Lines);
end;

Thanks a lot!

Fusspot answered 29/10, 2013 at 18:55 Comment(0)
L
2
sl.Text := StringReplace(S, sl.Delimiter, sLineBreak, [rfReplaceAll]);
Lavaliere answered 22/5, 2013 at 15:31 Comment(1)
You should probably add an explanation on what this is doing.Abrego
M
0
var
  MyString: String;
  Splitted: TArray<String>;

begin
  MyString := String.Join(',', ['String1', 'String2', 'String3']);
  Splitted := MyString.Split([','], 2);
end.

options:

Count : Maximum number of splits to return; defaults to MaxInt if not specified.

QuoteStart/QuoteEnd : Start and end characters of a quoted part of the string where the separator is ignored.

Options : Controls if any empty matches, or if trailing empty matches are included.

More info

Matelote answered 31/7, 2022 at 15:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.