Удаление определенных строк в памятке

У меня есть заметка с нежелательными символами в строках, и я хочу удалить их все. Вот мой код:

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;

Я считаю, что это неправильно, и я не должен этого делать. Как сделать такую ​​работу элегантно?


person Bianca    schedule 26.10.2014    source источник


Ответы (3)


Поскольку ваш цикл выполняется от 0 до Count - 1, строка после удаленной строки будет пропущена.

Объяснение: предположим, что строку 3 нужно удалить. Вы удаляете его, и теперь строка 4 будет строкой 3. Переменная цикла i будет увеличена до 4 при следующем запуске, поэтому новая строка 3 никогда не оценивается.

Решение: запустите цикл в обратном порядке:

for i := m0.Lines.Count - 1 downto 0 do
person NGLN    schedule 26.10.2014

Когда вы удаляете строку, вы меняете индекс всех последующих строк. Строки, которые вам еще предстоит обработать. Вы также аннулируете свой цикл, потому что после удаления строки верхний предел цикла for выходит за пределы. Ваш первый блок кода читается за пределами конца списка.

Рассмотрим список из трех строк. Вы смотрите на первую строку, индекс 0, и решаете удалить ее. Остались две строчки. Затем вам нужно взглянуть на строки 1 и 2 из исходного списка, но теперь они пронумерованы 0 и 1. Ваш цикл не выполняет свою работу. Вы пропустите строку с новым индексом 0.

Стандартный прием - обрабатывать список в обратном порядке. Затем, когда вы удаляете элемент, строки, изменение индексов которых уже были обработаны. В псевдокоде:

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

Ключевым моментом является то, что всякий раз, когда вы используете индекс i, вы имеете в виду элемент, который имел индекс i перед запуском цикла.

person David Heffernan    schedule 26.10.2014
comment
Мой любимый наставник, как всегда, ваши объяснения подробны. Теперь я понимаю эти line вещи. Хорошего воскресенья. - person Bianca; 26.10.2014

Эта задача требует другого подхода. Цикл 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;
person mg30rg    schedule 28.10.2014