Reputation: 973
I have memo with unwanted character in lines, and I want to remove them all. Here is my code:
var
del: Integer;
begin
for del := 0 to m0.Lines.Count - 1 do
begin
if (AnsiContainsStr(m0.Lines[del], 'remove me')) then
begin
m0.Lines.Delete(del);
end;
end;
end;
With the code above still left some lines that i wanted to remove. It only delete some of them. So I tried with another approach and this is do the job.
var
i, r, n: Integer;
begin
for i := 0 to m0.Lines.Count - 1 do
begin
if (AnsiContainsStr(m0.Lines[i], 'remove me')) then
begin
for r := 0 to m0.Lines.Count - 1 do
begin
if (AnsiContainsStr(m0.Lines[r], 'remove me')) then
begin
for n := 0 to m0.Lines.Count - 1 do
begin
if (AnsiContainsStr(m0.Lines[n], 'remove me')) then
begin
m0.Lines.Delete(n);
end;
end;
m0.Lines.Delete(r);
end;
end;
m0.Lines.Delete(i);
end;
end;
end;
I think this is not right, and i should not do this. How to do such job elegantly ?
Upvotes: 1
Views: 4422
Reputation: 1349
This task ask for another approach. A for cycle will dynamically inrease line indexes and causes lines after the deleted ones to be skipped because their indexes decrease.
You should use a while loop instead like:
intIndex := 0; // starting at the first line
while intIndex < m0.Lines.Count do // iterating 'till the last line
begin
if (AnsiContainsStr(m0.Lines[intIndex], 'remove me')) then // if the current line contains the text
m0.Lines.Delete(intIndex) // delete that line and DON'T increase the index
else
Inc(intIndex); // increase the index
end;
Upvotes: 2
Reputation: 613053
When you delete a line, you change the index of all subsequent lines. Lines that you are yet to process. You also invalidate your loop because once you delete a line the upper limit on the for
loop is out of bounds. Your first block of code reads beyond the end of the list.
Consider a list with 3 lines. You look at the first line, index 0, and choose to delete it. Now there are two lines left. You next need to look at lines 1 and 2 from original list, but they are now numbered 0 and 1. Your loop won't do the job. You will skip over the line that is newly indexed 0.
The standard trick is to process the list in reverse order. Then when you delete an item the lines whose indices change have already been processed. In pseudo-code:
for i := Count-1 downto 0 do
if DeleteThisItem(i) then
Delete(i);
The key point is that whenever you use index i
then you are referring to the item that had index i
before the loop started.
Upvotes: 3
Reputation: 43659
Because your loop runs from 0
to Count - 1
, the line after a deleted line will be skipped.
Explanation: suppose line 3 needs to get deleted. You delete it, and now line 4 will be line 3. The loop variable i
will be incremented to 4 on the next run, thus the new line 3 is never evaluated.
Solution: run your loop in reverse:
for i := m0.Lines.Count - 1 downto 0 do
Upvotes: 9