Reputation: 2049
I wanted to parse a ASCII Text for all occurences of e.g. ( bla, bla, bla ), text inside brackets using the following code
function FilterText(const Source, TagStart, TagEnd: string): TstringList; overload;
var StartPos, EndPos: integer;
begin
result.Clear;
try
StartPos := PosEx(TagStart,Source);
while StartPos > 0 do
begin
inc(StartPos,Length(TagStart));
EndPos := PosEx(TagEnd,Source,StartPos);
result.Add(Copy(Source,StartPos,EndPos - StartPos));
StartPos := PosEx(TagStart,Source,EndPos + Length(TagEnd));
end;
finally
end;
end;
I call this function using the following code
memo1.Lines.AddStrings( FilterText(memo1.Lines.Text,'(',')'));
Instead of returning all text between brackets I get an AV .... why ????
Upvotes: 1
Views: 363
Reputation: 613382
This line:
result.Clear;
Calls a method on an unitialized variable of type TStringList
. That leads to undefined behaviour. In your case it seems that manifests as an access violation.
You could instantiate the string list inside FilterText
, and so avoid the AV.
function FilterText(...): TStringList;
begin
result := TStringList.Create;
try
....
except
result.Free;
raise;
end;
end;
But that leaves you with a bigger problem. Who is going to take ownership of the object? You use the function like this:
memo1.Lines.AddStrings(FilterText(memo1.Lines.Text,'(',')'));
Since your design has FilterText
returning a new string list object, nothing takes ownership and therefore you leak.
You could do it like this:
FilteredText := FilterText(...);
try
memo1.Lines.AddStrings(FilteredText);
finally
FilteredText.Free;
end;
Since FilterText
is instantiating a new object and expecting the caller to take ownership, it would be prudent to give it a name to indicate that ownership transfer. For example, you might call the function CreateFilteredText
.
Another option would be to expect the caller to instantiate the string list, and pass that object to FilterText
. I personally find that approach to be a little easier to understand.
That version might look like this:
procedure FilterText(..., FilteredText: TStrings);
begin
...
Strings.Add(...);
...
end;
And from the caller's side:
FilteredText := TStringList.Create;
try
FilterText(..., FilteredText);
memo1.Lines.AddStrings(FilteredText);
finally
FilteredText.Free;
end;
Upvotes: 3
Reputation: 80287
You forgot to create Result (TStringList).
You can also use TStrings (more general type than TStringList) argument instead of result like
procedure FilterText(const Source, TagStart, TagEnd: string; AStrings: TStrings);
so external code will responsible both for creation and for deletion of list.
Upvotes: 1
Reputation: 125749
Your function returns a TStringList, but never creates it. (It clears it instead, which is not necessary.)
begin
Result := TStringList.Create;
...
end;
I personally don't like this, as it means the memory is allocated in the function but has to be freed by the code calling it. I prefer to have the stringlist passed in as a parameter to the function instead, so that it's clear that the caller both has to allocate it and free it.
Upvotes: 1