Getting Access Violation in Listview

I am getting an Access Violation when importing a CSV file. The Stringlist leaves many white spaces in the tlistview after transferring the data from a Memo. What I want to know is why I am getting this access violation and is there a better way to go about this code to ellivate the extra loops i am putting the program through? Thanks! test.csv
procedure TForm1.Button1Click(Sender: TObject);

var
l,i,j : integer;
Lst : TStringList;

begin
Memo1.Lines.Clear;
ListView1.Items.Clear;
If OpenDialog1.Execute then
Memo1.Lines.LoadFromFile(OpenDialog1.FileName);

Lst := TStringList.Create;
    Lst.LoadFromFile(OpenDialog1.FileName);
    Lst.Delete(0);
    Memo1.Lines.StrictDelimiter := True;
    Memo1.Lines.DelimitedText := Lst.Text;

for i := 0 to Memo1.Lines.Count do
If (Memo1.Lines[i] = '')then
Memo1.Lines.Delete(i);

for i := 0 to Memo1.Lines.Count do
    begin
      With ListView1.Items.Add do
      begin
        Caption := Memo1.Lines[i * ListView1.Columns.Count];
      for j := 1 to ListView1.Columns.Count do
      if ((i * ListView1.Columns.Count) + j) < (Memo1.Lines.Count) then
            SubItems.Add(Memo1.Lines[i * ListView1.Columns.Count + j])
          else
            Break;

for l := 1 to ListView1.Items.Count -1 do
If (ListView1.Items[i].Caption = '') then
ListView1.Items[i].Delete;

ListView1.Column[0].Width := -1;
ListView1.Column[1].Width := -1;
ListView1.Column[2].Width := -1;
ListView1.Column[3].Width := -1;
ListView1.Column[4].Width := 55;
ListView1.Column[5].Width := -1;
ListView1.Column[6].Width := -1;
ListView1.Column[7].Width := -1;


end;
end;
end;

Open in new window

DelphiNubeAsked:
Who is Participating?

Improve company productivity with a Business Account.Sign Up

x
 
Lester_ClaytonConnect With a Mentor Commented:
for i := 0 to Memo1.Lines.Count do

Use for i := 0 to Pred(Memo1.Lines.Count) do

Explanation : if Lines.Count = 5 then your statement tries to do 6 lines (0, 1, 2, 3, 4, 5).  It will fail on the last one.

Be sure to change this on all applicable lines.

Also, be sure to do Lst.Free at the end to free up the allocated memory.

Finally, if you're going to delete items in a ListView, like you are, do it BACKWARDS.

Example:

for l := Pred(ListView1.Items.Count) downto 0 do

The reason is, if you have 10 items, and your program is going from item 0 to 9 (all 10), and it deletes item 2 - then item 9 is no longer valid.  If you delete item 4, then item 8 is no longer valid - it all moves up one.  Doing it backwards is much much safer.
0
 
epasquierConnect With a Mentor Commented:
when you are going to delete items, and you still want the parsing to occurs in the normal way, you can use good old While loops
This is a strict equivalent to for i:=0 to Items.Count-1 do ... ;
i:=0;
While i<Items.Count do 
 begin
  ...
  Inc(i);
 end;

Open in new window

It will prevent potential problems when changing the Items list.

Specifically to your problem, you might not want to expand completely the CSV file, and delete the empty lines - that could delete an empty field, and make all of your code shifting the values.
Besides, that is not optimized

Instead, treat each CSV line one after the other. Only discard empty lines (and not empty fields)
Then, it is good idea to add even empty fields in the SubItems, to avoid testing the SubItems.Count each time you want to access a field from the listview.


procedure TForm1.Button1Click(Sender: TObject);
begin
 If OpenDialog1.Execute then
  ParseCSVFile(OpenDialog1.FileName);
end;

procedure TForm1.ParseCSVFile(FN:String);
var
 Lines, Values : TStringList;
 i,j:integer;
 L:String;
begin
 Lines:=TStringList.Create;
 Values:=TStringList.Create;
 
 ListView1.Items.BeginUpdate; // will prevent drawing of new items while building the list 
 try
  ListView1.Items.Clear;
  Lines.LoadFromFile(FN);
  Lines.Delete(0); // Delete first line of CSV file (Fields Header)
  i:=0;
  While i<Lines.Count do
   begin
    L:=Trim(Lines[i]); // get the line and trim leading/tailing spaces
    if L<> '' Then  // treat only not empty lines
     begin
      Values.DelimitedText:=Lines[i];  // Split this line values only
      if Values[0]<>'' Then // Will create only items with first field not empty
       With ListView1.Items.Add do
        begin
         Caption := Values[0];
         for j := 1 to ListView1.Columns.Count-1 do
          if j<Values.Count 
           Then SubItems.Add(Values[j])
           Else SubItems.Add(''); // Or break, but adding a SubItem even for empty fields 
           // can help later (you can avoid some "index out of bounds" exceptions 
           // by making sure that every item has the same number of subItems)
        end;
     end;   
    Inc(i);
   end;
 finally
  ListView1.Items.EndUpdate;   // will finally update the listview
  Lines.Free;
  Values.Free;
 end;  
// That should be done only once, maybe in OnFormCreate event 
 With ListView1 do
  begin 
   for i:=0 to 7 do Column[i].Width := -1; 
   Column[4].Width := 55; 
  end;    
end;

Open in new window

0
 
Geert GOracle dbaCommented:
why must you use math ?


Caption := Memo1.Lines[i * ListView1.Columns.Count];

Caption := Memo1.Lines[i];

Open in new window

0
 
epasquierCommented:
because he expanded the whole CSV file  with this code Memo1.Lines.DelimitedText := Lst.Text;

L1C1,L1C2,L1C3
L2C1,L2C2,L2C3
=>
L1C1
L1C2
L1C3
L2C1
L2C2
L2C3

As I said, it's not a good idea, it makes the problem more complex and can create bugs if the CSV file has some lines with missing fields
0
 
DelphiNubeAuthor Commented:
Thanks fellow coders! Lester: You helped me use a system commend that I have never use that stabilized my code. epasquier: You helped me to use a routine that i'm very familiar with plus helped sustain the integrity of the existing code.

Thanks to both of you!
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.