Существуют ли передовые практики, позволяющие избежать ошибки индекса за пределами границ при циклическом отображении элементов TStringList? - PullRequest
1 голос
/ 13 декабря 2011

:)

Во-первых, мой код

procedure TForm1.Button3Click(Sender: TObject);
var tempId,i:integer;
begin
tempId:=strtoint(edit5.Text);
plik:=TStringList.Create;
plik.LoadFromFile('.\klienci\'+linia_klient[id+1]+'.txt');
if (plik.Count=1) then
  begin
  label6.Caption:='then';
    if (tempId=StrToInt(plik[0])) then
      begin
      Label6.Caption:='Zwrócono';
      plik.Delete(0);
    end
  end
else
for i:=0 to plik.Count-2 do
  begin
    if (tempId=StrToInt(plik[i])) then
    begin
      Label6.Caption:='Zwrócono';
      plik.Delete(i);
    end;
  end;
plik.SaveToFile('.\klienci\'+linia_klient[id+1]+'.txt');
plik.Free;
end;
  • Когда for i:=0 to plik.Count-2 do Я могу удалить любой элемент, но не последний.
  • Когда for i:=0 to plik.Count-1 do я могу удалить любой элемент без но от конца к началу. Потому что иначе Список индексов выходит за пределы .

Что происходит? Как я могу безопасно искать и удалять элементы из TStringList?

Ответы [ 5 ]

8 голосов
/ 13 декабря 2011

При удалении целых чисел из списка вы хотите использовать цикл downto, т. Е.

for i := plik.Count-1 downto 0 do
  begin
    if (tempId=StrToInt(plik[i])) then
    begin
      Label6.Caption:='Zwrócono';
      plik.Delete(i);
    end;
  end;

Это гарантирует, что если вы удаляете элемент, индекс цикла остается в силе при перемещении от конца списка вниз.начало списка.

5 голосов
/ 13 декабря 2011

Это классическая проблема.Цикл for оценивает границы цикла один раз в начале цикла, поэтому вы запустите конец, который объясняет ошибки индекса за пределами границ.

Но даже если for зацикливает вычисленные границы цикла каждый разкак while это не очень поможетКогда вы удаляете элемент, вы уменьшаете Count на 1 и перемещаете оставшиеся элементы вниз на один в списке.Таким образом, вы изменяете индекс всех тех элементов, которые еще должны быть обработаны.

Стандартный трюк заключается в циклическом циклическом просмотре списка:

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

Когда вы пишете его таким образом, вызов Delete влияет на индексы элементов, которые уже обработаны .

2 голосов
/ 13 декабря 2011

Как уже говорили другие, лучше всего использовать цикл downto.Конечно, это меняет семантику цикла, поэтому он работает в обратном направлении, а не в прямом направлении.Если вы хотите продолжить цикл вперед, вы должны использовать цикл while, например:

I := 0;
while I < plik.Count do 
begin 
  if (tempId = StrToInt(plik[I])) then 
  begin 
    ...
    plik.Delete(I); 
  end else
    Inc(I); 
end; 

или:

var
  CurIdx, Cnt: Integer;

CurIdx := 0;
Cnt := plik.Count;
for I := 0 to Cnt-1 do 
begin 
  if (tempId = StrToInt(plik[CurIdx])) then 
  begin 
    ...
    plik.Delete(CurIdx); 
  end else
    Inc(CurIdx); 
end; 
2 голосов
/ 13 декабря 2011

в восходящем цикле, например for i:=1 to count, вы просто не можете удалить элементы списка, по которому вы перебираете.

Есть несколько решений в зависимости от общей логики того, чего вы хотите достичь.

  1. вы можете изменить цикл for на цикл while, который переоценивает count и не увеличивает индекс на итерации удаления

  2. Вы можете изменить цикл, вроде for i:=count downto 1

  3. вместо delete, вы можете создать временный список и скопировать туда только те элементы, которые вы хотите сохранить, и скопировать его обратно.

2 голосов
/ 13 декабря 2011
For I := stringlist.count-1 downto 0 do

Теперь вы можете удалить все элементы без ошибок

...