1

我有行中有不需要的字符的备忘录,我想将它们全部删除。这是我的代码:

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;

我认为这是不对的,我不应该这样做。如何优雅地完成这样的工作?

4

3 回答 3

9

因为您的循环从0to运行Count - 1,所以将跳过已删除行之后的行。

说明:假设需要删除第 3 行。您删除它,现在第 4 行将成为第 3 行。循环变量i将在下一次运行时增加到 4,因此永远不会评估新的第 3 行。

解决方案:反向运行循环:

for i := m0.Lines.Count - 1 downto 0 do
于 2014-10-26T08:06:25.600 回答
3

删除一行时,会更改所有后续行的索引。您尚未处理的行。您还会使循环无效,因为一旦删除了一行,循环的上限for就会超出范围。您的第一个代码块超出了列表的末尾。

考虑一个包含 3 行的列表。您查看第一行,索引 0,然后选择删除它。现在还剩下两行。接下来,您需要查看原始列表中的第 1 行和第 2 行,但它们现在编号为 0 和 1。您的循环无法完成这项工作。您将跳过新索引为 0 的行。

标准技巧是以相反的顺序处理列表。然后,当您删除一个项目时,其索引更改的行已被处理。在伪代码中:

for i := Count-1 downto 0 do
  if DeleteThisItem(i) then
    Delete(i);

关键点是,每当您使用索引时,i您指的是i在循环开始之前具有索引的项目。

于 2014-10-26T08:10:49.940 回答
2

这个任务要求另一种方法。for 循环将动态增加行索引,并导致已删除行之后的行被跳过,因为它们的索引减少。


您应该使用 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;
于 2014-10-28T11:56:11.600 回答