Link to home
Start Free TrialLog in
Avatar of New2Delphi
New2Delphi

asked on

StringList Problem! Please help!

Hi! I seem to have a problem with the following code. I always get an "Out of Bounds" error. I don't know why it does that. Can someone help me out. Thanks!

procedure TForm1.Button1Click(Sender: TObject);
var
  i: Integer;
  S: TStringList;
  Del: TListItem;
  DelIndex: String;
begin
  if ListView1.Selected = nil then
    Exit;

  S := TStringList.Create;
  Del:= ListView1.Selected;

  try
    S.LoadFromFile(SaveDir + 'Tools.txt');
  except
    S.Free;
    Exit;
  end;

  while Del <> nil do
    begin
      with ListView1 do
        begin
          for i := 0 to S.Count - 1 do
            begin
              if S.Strings[i] = (Selected.Caption + '=' + Selected.SubItems[i]) then
                begin
                  DelIndex := S.Strings[i];
                  S.Delete(S.IndexOf(DelIndex));
                end;
            end;

          Items.Delete(Items.IndexOf(Del));
        end;

      Del:= ListView1.GetNextItem(nil, sdAll, [isSelected]);
    end;

  S.SaveToFile(SaveDir + 'Tools.txt');
  S.Free;
end;
Avatar of scrapdog
scrapdog
Flag of United States of America image

You are iterating through the StringList and deleting items from it in the same loop.

"S.Count - 1" in the for loop is evaluated at the beginning of the loop, not for every iteration.

If you want to reevaluate on every iteration (because S.Count changes after a delete), then use a "while" loop instead of a for loop.
ASKER CERTIFIED SOLUTION
Avatar of scrapdog
scrapdog
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
or do it from bottom:

for i := S.Count - 1 downto 0 do
  S.Delete(i);

Avatar of d32coder
d32coder

You should also only have S.Free; in one place (at the end).  This will help prevent an access to a stringlist that no longer exists.  

Dragon is right, loop backwards.



procedure
var .....
begin

if filexists(savedir+fiename) then
 begin
 loadfromfile(savedir+fiename)
 
 // loop here

 end;
 
 S.Free;
end;
As long as we're talking about S.Free

try
   S.LoadFromFile(SaveDir + 'Tools.txt');
 except
   S.Free;
   Exit;
 end;


You should probably use try..finally instead of try..except.  Place the "try" before the S.LoadFromFile, and the "finally" right after S.SaveToFile.

It is not a good idea to call Exit in an exception handler;  it is better to call "raise".  (You wouldn't need to do that when you use finally).
hello,

i think error msg ( index out of bound ) is not for <S> ( as a StringList ) . it is for <Selected.SubItems[i]>. ( although it can be for S , if just tools.txt was empty ).

this becouse of that the selected Item has not a SubItem. if the selected item has it . there is not eny error msg!

that is better he write :

if Selected.SubItems.Count <> 0 then
...
...
...

best regards
hamed
Selected.Subitems.count must be equal to S.Count ( at least )
Avatar of New2Delphi

ASKER

Hmmm... what if I want to edit a string in the list? What procedure should I use?
You mean string in the StringList?

e.g. You want to change value for String #3:

var
  MyString: string;
begin
  MyString := StringList.Strings[2];
  if InputQuery('StringList', 'Enter new value',
    MyString) then
    StringList.Strings[2] := MyString;
...



Edit a String at StringList or Edit(add) a child as Subitem? although there is not so difference.(i think DragonSlayer comment is good)

what is ur idea about my last comment? do u agree with me?

hamed ;-)
DragonSlayer's comment about doing it from bottom worked fine.

Now I have a problem with editing. See what my program does is take website addresses from a textfile, load it into a TStringList and then load the data into a TListView. Now if I want to edit, add or delete it, I just go back to the StringList and do the neccessary function on it. The code I posted was the function to delete an item from the TStringList. It compares the item in the TStringList with the selected item of the TListView and if they are the same, it deletes it from the TStringList and TListView. Also, the code I posted had a typo. Selected.SubItems[i] should be Selected.SubItems[0] since I only have one sub-item.

Anyway, when editing, what I want to do is load the item from my TListView onto 2 Edit boxes, edit the data from there, and then update the TStringList. Is this possible? Can you please post sample code for this. Thanks!
Please help me.
I think I found your mistake :

when u delete one string from S then Count decrese one, there fore u must refresh the for loop ( becouse if u not i passes end of Items of S )

now this is the corrected code :

 while Del <> nil do
   begin
     with ListView1 do
       begin
        ExitCode := False;

         while not Exitcode do
         begin
         ExitCode := True;
         for i := 0 to S.Count - 1 do
           begin
             if S.Strings[i] = (Selected.Caption + '=' + Selected.SubItems[0]) then
               begin
                 DelIndex := S.Strings[i];
                 S.Delete(S.IndexOf(DelIndex));
                 ExitCode := False;
                 break;
               end;
           end; // for
         end; // while

         Items.Delete(Items.IndexOf(Del));
       end;

     Del:= ListView1.GetNextItem(nil, sdAll, [isSelected]);
   end;

after each delete i refresh the for loop

best regards
hamed
New2Delphi , are you here. Could last code solve your problem ? ;-)
Hi! When editing, what I want to do is load the selected item from my TListView onto 2 Edit boxes, edit the
data from there, and then update the TStringList. Is this possible? Can you please post sample code
for this. Thanks!
hi New2Delphi , how are you?

lets do step by step ( I am not shur i understand exactly that u want ) :

1. u want load selected item from TListView to 2 Edit Boxes.

Edit1.Text := Selected.Caption;
Edit2.Text := Selected.Caption;

i think u mean u want to load subitem to another EditBox.then :

Edit1.Text := Selected.Caption;
Edit2.Text := Selected.Selected.SubItems[0];

2.now you can Edit them in Edit Boxes

3.at the end you want to Update your TStringList ( I guess you mean <S> witch is a TStringList ):


if S.Strings[i] = (Edit1.Text + '=' + Edit2.Text) then
              begin
                DelIndex := S.Strings[i];
                S.Delete(S.IndexOf(DelIndex));
                ExitCode := False;
                break;
              end;
         

I think your goal is stile not so clear for me. is that 3 step usefull your u.

I think that is better we use a real time communication . chat is a good way but i have not ICQ. just I have Yahoomessanger in my computer . also i have not ID in that ( Yaho... used by my brother and he is not here know ) if you know a easy chat room ( that means we dont need any package for use it ) please tell me . we will design the time and will use that chatroom.

i think by chat room you can find your need very rapidly.

also other experts can attach us on that chat room.

best regards
hamed

















sorry, but i can not understand what u say! please tell me clearer. lets do step by step.

1. can it solve your last problem. that means is it stile related to that error message or not ?

2. if it is a new problem please repeat the question. with code and exactly point to line that you have problem!

3. I can not see where is your editing code! in your last codes.

best regards
hamed

the text in the end is the last version of my comment. i wanted to send it becouse i can not understand what is your need. but read it again and write my main comment that you can read in start.

best regards
hamed
hamed, sorry if I don't make any sense. Anyway, deleting from the StringList is not a problem anymore. My problem is editing a string from the StringList. I do not have sample code because I don't know if this is possible.

What my program does is load the contents of the StringList to a ListView. When I want to edit a string I load it from the ListView into 2 edit boxes:

Edit1.Text := Selected.Caption;
Edit2.Text := Selected.Selected.SubItems[0];

And then I edit it as necessary. Now, when I click on the "Save" button what I want to do is save the edited string back to the StringList so that the old string will be replaced with the edited one? Can this be done? Can you post code that will allow me to do this? Thanks!
hamed, sorry if I don't make any sense. Anyway, deleting from the StringList is not a problem anymore. My problem is editing a string from the StringList. I do not have sample code because I don't know if this is possible.

What my program does is load the contents of the StringList to a ListView. When I want to edit a string I load it from the ListView into 2 edit boxes:

Edit1.Text := Selected.Caption;
Edit2.Text := Selected.Selected.SubItems[0];

And then I edit it as necessary. Now, when I click on the "Save" button what I want to do is save the edited string back to the StringList so that the old string will be replaced with the edited one? Can this be done? Can you post code that will allow me to do this? Thanks!
is it mean you want to save S ( as StringList ) to the original txt file ?

that means :

1. you load S from txt file
2. Edit S ( as StringList ) in any way that u want
3. restore S to txt file by save button

now u can do step 1, and step 2. is ur problem to do step 3. that mean u want to know how u can store again the Updated S to the txt file?

best regards
hamed
Yes! That is exactly my problem. Can you help me?
S.SaveToFile(FileName);
oh, he was so faster than me .like Loadfromfile you can use Savetofile.

best regards
hamed
Yes, I know you have to save it to file. What I want to know is if the edited string will be saved with the same index number and if not, can you specify waht code I can use to do so. Thanks!

e.g.

If the original string is Test12=1234567 with an index of 3 and I change it to Test13=4567890 if I save it will it still be in the same index number?
if you changed it using S.Strings[3] := 'Test13=4567890' then it will be saved with the same index number, IF you did not sort it.
What if it's S.Strings[i] since I do not know for sure what the index is?
e.g.

Edit1.Text := S.Strings[i];
// some editing
// value of i did not change
S.Strings[i] := Edit1.Text

will be fine
i am stile mixture! ;-) the savetofile method save S in .txt file in the same order of its item . now do you wont for example save .txt file with sorted form? like this
Test1 = ...
Test2 = ...
Test3 = ...
Test4 = ...
..
.

after change. if u want it . you must at first Set S before saveing and when S updated in your need form you can use savetofile.

if it is your problem i think you can use sort Items in S. or somthing like this!

if i am still in wrong way please tell me ! dont worry about our time we are here to help each other .

best regards
hamed
New2Delphi,what was happened? ;-) maybe you become tired becouse we can not understand all your your problems  exactly! :-( . if you want send me your App, email address : h_mohsenian@yahoo.com & mohsenianrad@scribe.com and discribe your problem with code. also if you want tell me more about yourself . I saw your profile , but there were not any thing about you!

Best regards
Hamed
I'll post the results tomorrow. :-)
ok! ;-) but witch results? do you talk about your code ? , your profile ? or ... ?  :-)

good coding
hamed
Yes, the code I use.
Avatar of Russell Libby
No comment has been added lately, so it's time to clean up this TA.
I will leave a recommendation in the Cleanup topic area that this question is:

Accept scrapdog's comment as answer

Note:
This recommendation is based on the "original" question asked, and the points offered does not warrant a split. My apologies to the other experts.

Please leave any comments here within the next seven days.
 
PLEASE DO NOT ACCEPT THIS COMMENT AS AN ANSWER!
 
Thank you,
Russell

EE Cleanup Volunteer