在循环TStringList项时,是否存在避免越界索引错误的良好实践?

时间:2011-12-13 11:19:53

标签: delphi pascal tstringlist

:)

首先,我的代码

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 个答案:

答案 0 :(得分:8)

从列表中删除intems时,要使用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;

这确保了如果删除项目,当您从列表的列表末尾开始移动列表时,循环索引将保持有效。

答案 1 :(得分:5)

这是一个经典问题。 for循环在循环开始时对循环边界进行一次计算,因此您可以在结束时运行,这将解释索引超出范围的错误。

但是,即使for循环每次像while那样评估循环边界,也不会真正有用。删除元素时,将Count减1并将其余元素向下移动到列表中的一个元素。因此,您可以更改所有仍处理元素的索引。

标准技巧是循环列表:

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

以这种方式编写时,对Delete的调用会影响已经处理过的元素的索引。

答案 2 :(得分:2)

For I := stringlist.count-1 downto 0 do

现在您可以删除所有项目而不会出现任何错误

答案 3 :(得分:2)

在像for i:=1 to count这样的升序循环中,您无法删除正在迭代的列表中的项目。

根据您想要实现的整体逻辑,有几种解决方案。

  1. 您可以将for循环更改为重新评估while的{​​{1}}循环,并且不会在删除迭代中增加索引

  2. 你可以改变循环,有点count

  3. 而不是for i:=count downto 1,您可以创建一个临时列表,只复制您要保留的项目,然后重新复制。

答案 4 :(得分:2)

正如其他人所说,使用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;