2014-10-26 2 views
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; 

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

ответ

8

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

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

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

for i := m0.Lines.Count - 1 downto 0 do 
+0

Краткий и очень ясное объяснение. Спасибо! – Bianca

3

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

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

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

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

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

+2

Мой любимый наставник, как всегда, ваше объяснение - деталь. Теперь я понимаю эту «линию». Хорошего воскресенья. – Bianca

+0

Спасибо. И хороший день для вас тоже. –

2

Эта задача требует другого подхода. A для цикла будет динамически увеличивать индексы строк и заставляет строки после удаленных пропустить, поскольку их индексы уменьшаются.


Вы должны использовать время цикла вместо как:

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; 
Смежные вопросы