我有一个行中有不需要字符的备忘录,我想把它们全部删除。这是我的代码:
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;
上面的代码仍然留下了一些我想删除的行。它只删除了其中的一部分。所以我尝试了另一种方法,这就是做好工作。
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;
我认为这是不对的,我不应该这样做。如何优雅地完成这样的工作?
因为循环从0
运行到Count - 1
,所以删除行之后的行将被跳过。
说明:假设第3行需要删除。你删除它,现在第4行将是第3行。循环变量i
将在下一次运行时增加到4,因此永远不会对新行3进行求值。
解决方案:反向运行循环:
for i := m0.Lines.Count - 1 downto 0 do
删除一行时,将更改所有后续行的索引。您尚未处理的行。您还可以使循环无效,因为一旦删除一行,for
循环的上限就超出了界限。您的第一个代码块读取的内容超出了列表的末尾。
考虑一个有3行的列表。你看第一行,索引0,然后选择删除它。现在还剩两行。接下来,您需要查看原始列表中的第1行和第2行,但它们现在编号为0和1。你的循环不起作用。您将跳过新索引为0的行。
标准的技巧是按相反的顺序处理列表。然后,当您删除一个项目时,其索引更改的行已经被处理。在伪代码中:
for i := Count-1 downto 0 do
if DeleteThisItem(i) then
Delete(i);
关键是,无论何时使用索引i
,都是指在循环开始之前具有索引i
的项。
此任务要求采用另一种方法A for cycle将动态地减少行索引,并导致删除行之后的行被跳过,因为它们的索引会减少。
你应该使用while循环来代替:
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;