57

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?

Blorgbeard
  • 93,378
  • 43
  • 217
  • 263
Acron
  • 1,308
  • 3
  • 19
  • 32
  • What version of Delphi are you working in? – Roee Adler Aug 26 '09 at 14:27
  • possible duplicate of [Split a string into an array of strings based on a delimiter](http://stackoverflow.com/questions/2625707/split-a-string-into-an-array-of-strings-based-on-a-delimiter) – Z80 Nov 26 '13 at 17:20
  • I've never been more angry with a language bug than I am right now. Thank you, Acron, for bringing this to my attention. – Chucky Jan 09 '14 at 17:21

5 Answers5

89

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 :)

Community
  • 1
  • 1
Roee Adler
  • 31,157
  • 31
  • 99
  • 132
  • 1
    What do you want to work around? Looks like a solution to me. – jpfollenius Aug 26 '09 at 14:51
  • 4
    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. – Todd Aug 26 '09 at 15:04
  • 3
    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'. :-) – robsoft Aug 26 '09 at 15:17
  • @Todd - That was the first significant sub-class I ever wrote in Delphi 3! – Gerry Coll Aug 27 '09 at 09:13
19
sl.DelimitedText := '"' + StringReplace(S, sl.Delimiter, '"' + sl.Delimiter + '"', [rfReplaceAll]) + '"';
Alex
  • 5,245
  • 2
  • 33
  • 52
  • 2
    Crap, you beat me, I was typing exactly the same answer :) – The_Fox Aug 26 '09 at 16:01
  • Very clever, thank you Alexander, aswell as The_Fox, even if you diddn't help me directly, but you intended to do so :) – Acron Aug 26 '09 at 21:35
  • 4
    +1 Very nice, but has a problem if the original string contains a subsection like ' " " ' (i.e. inverted commas and spaces in the text) – Roee Adler Aug 27 '09 at 05:12
  • >>> has a problem if the original string contains a subsection like ' " " ' And how can user name contain '"' char? :) – Alex Aug 27 '09 at 05:18
  • 1
    @Alexander - Here's an example: 'Bill "The Womanizer" Clinton' :) – Roee Adler Aug 27 '09 at 12:14
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);

Community
  • 1
  • 1
Kashif Raja
  • 61
  • 1
  • 2
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!

Ale Costa
  • 41
  • 3
2
sl.Text := StringReplace(S, sl.Delimiter, sLineBreak, [rfReplaceAll]);
barbaris
  • 499
  • 7
  • 25