Solved

Dynamic Array problem. For Madshi or AvonWyss

Posted on 2002-04-11
22
225 Views
Last Modified: 2010-04-04
I decided to create a new unit with the code you guys supplied. The problem is, I get an error saying
something about TMyStringList not being initialized. Could you tell me what I'm doing wrong. The code for the new unit I created is below. Thanks!





unit NewStringList;

interface

uses Windows;
type
 TStringArray = record
   Size: Integer;
   Capacity: Integer;
   Items: Array of String;
 end;

 TMyStringList = class(TObject)
   List: TStringArray;
//    constructor Create; //Don't know how to do this!
//    destructor Destroy; //Don't know how to do this!
   procedure Add(Item: String);
   procedure Delete(Index: Integer);
   function Count: Integer;
   procedure LoadFromFile(FileName: String);
   procedure SaveToFile(FileName: String);
 end;

implementation

constructor TMyStringList.Create;
begin
//Don't know how to do this!
end;

destructor TMyStringList.Destroy;
begin
//Don't know how to do this!
end;

procedure TMyStringList.Add(Item: String);
begin
 if List.Size = List.Capacity then
   begin
     if List.Capacity = 0 then
       List.Capacity := 64
     else
       List.Capacity := List.Capacity * 3 div 2;

     SetLength(List.Items, List.Capacity);
   end;

 List.Items[List.Size] := Item;

 Inc(List.Size);
end;

procedure TMyStringList.Delete(Index: Integer);
begin
 if (Index > 0) and (Index < List.Size) then
   begin
     Dec(List.Size);
     List.Items[Index] := '';

     if Index < List.Size then
       begin
         Move(List.Items[Index + 1], List.Items[Index], (List.Size - Index) * 4);
         Integer(List.Items[List.Size]) := 0;
       end;
   end;
end;

function TMyStringList.Count: Integer;
begin
Result := List.Size;
end;

procedure TMyStringList.LoadFromFile(FileName: String);
var
 InFile: Text;
 S: String;
begin
 AssignFile(InFile, FileName);
 Reset(InFile);

 while not EOF(InFile) do
   begin
     ReadLn(InFile, S);
     Add(S);
   end;

 CloseFile(InFile);
end;

procedure TMyStringList.SaveToFile(FileName: String);
var
 OutFile: Text;
 i: Integer;
begin
 AssignFile(OutFile,FileName);
 Rewrite(OutFile);

 for i := Low(List.Items) to High(List.Items) do
   WriteLn(OutFile, List.Items[i]);

 CloseFile(OutFile);
end;

end.
0
Comment
Question by:DelFreak
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 11
  • 7
  • 4
22 Comments
 
LVL 7

Expert Comment

by:God_Ares
ID: 6933660
unit unit2;

interface

uses Windows;
type
TStringArray = record
  Size: Integer;
  Capacity: Integer;
  Items: Array of String;
end;

TMyStringList = class
  List: TStringArray;
  constructor Create; //Don't know how to do this!
  destructor Destroy; override;
  procedure Add(Item: String);
  procedure Delete(Index: Integer);
  function Count: Integer;
  procedure LoadFromFile(FileName: String);
  procedure SaveToFile(FileName: String);
end;

implementation

constructor TMyStringList.Create;
begin
  with list do
  Begin
    Size:=0;
    Capacity:=0;
    Setlength(items,size);
  end;
end;

destructor TMyStringList.Destroy;
begin
  with list do
  Begin
    Size:=0;
    Capacity:=0;
    Setlength(items,size);
  end;
end;

procedure TMyStringList.Add(Item: String);
begin
if List.Size = List.Capacity then
  begin
    if List.Capacity = 0 then
      List.Capacity := 64
    else
      List.Capacity := List.Capacity * 3 div 2;

    SetLength(List.Items, List.Capacity);
  end;

List.Items[List.Size] := Item;

Inc(List.Size);
end;

procedure TMyStringList.Delete(Index: Integer);
begin
if (Index > 0) and (Index < List.Size) then
  begin
    Dec(List.Size);
    List.Items[Index] := '';

    if Index < List.Size then
      begin
        Move(List.Items[Index + 1], List.Items[Index], (List.Size - Index) * 4);
        Integer(List.Items[List.Size]) := 0;
      end;
  end;
end;

function TMyStringList.Count: Integer;
begin
Result := List.Size;
end;

procedure TMyStringList.LoadFromFile(FileName: String);
var
InFile: Text;
S: String;
begin
AssignFile(InFile, FileName);
Reset(InFile);

while not EOF(InFile) do
  begin
    ReadLn(InFile, S);
    Add(S);
  end;

CloseFile(InFile);
end;

procedure TMyStringList.SaveToFile(FileName: String);
var
OutFile: Text;
i: Integer;
begin
AssignFile(OutFile,FileName);
Rewrite(OutFile);

for i := Low(List.Items) to High(List.Items) do
  WriteLn(OutFile, List.Items[i]);

CloseFile(OutFile);
end;

end.

somthing like this... regards me

ps.

if you WARE using a stinglist you should create one in the
constructor "list := Tstringlist.create;"

destuctor and destory it in the destructor "list.free"

0
 
LVL 7

Expert Comment

by:God_Ares
ID: 6933663
if you are going to use your TMyStringList

do this

var mystinglist : TMyStringList ;

begin
  MyStringList := TMyStringList.create;
  ...
  ...
  TMyStringList.free;
end;
0
 

Author Comment

by:DelFreak
ID: 6933801
So TMyStringList should be = class and not = class(TObject)?
0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 7

Expert Comment

by:God_Ares
ID: 6934021
is the same...

anyway your TMyStringList not being initialized has more to do with


var
  x : TMyStringList;

begin
  x.add(...);  <-x wasn't created.



still got problems?
0
 
LVL 20

Expert Comment

by:Madshi
ID: 6936171
I go with God_Ares in that you probably simply forgot to instantiate the class. Please show us the part of the code where the exception was raised, and please mark the line where the exception occured exactly.

BTW, you don't need no constructor nor a destructor in this situation. Class variables are always initialized to 0, so the constructor is not needed. Also dynamic arrays are automatically freed by Delphi, so you don't need a destructor here, either. You can leave the code exactly as it is in your original question text. Just one little bug in the code:

In the SaveToFile method the loop has to look like this:

for i := 0 to List.Size - 1 do

E.g. if you add only one item to the list, it would be like this:

List.Size -> 1
List.Capacity -> 64
List.Items -> ['item1', '', '', '', '', '', [...], ''];
low(List.Items) -> 0
high(List.Items) -> 63

So if you loop to high(List.Items), you're saving 64 items, though you only have one in the list.

Regards, Madshi.

P.S: Usually one names class variables with a leading "F", so you should name it "FList", not "List". That's up to you, of course, just wanted to mention, what is usual...
0
 
LVL 7

Expert Comment

by:God_Ares
ID: 6936205
BTW, you don't need no constructor nor a destructor in this situation. Class variables are always initialized
to 0, so the constructor is not needed. Also dynamic arrays are automatically freed by Delphi, so you
don't need a destructor here, either.

Don't you just love Delphi... :)
0
 
LVL 20

Expert Comment

by:Madshi
ID: 6936211
Yeah, I do, really...   (-:
0
 

Author Comment

by:DelFreak
ID: 6939676
Well, the error I get is runtime error 2 at 00005073

This is my procedure for logging:

procedure AddLogEntry(S: String);
var
  SS, SSS: String;
  i: Integer;
begin
  with TStringList.Create do
    begin
      try
        LoadFromFile('Application.log');
      except
        SS := 'Application Log - Generated on ' + CurrentDateAndTime;

        Add(SS);
        Add('Process ID: '{ + GetProcessID(ParamStr(0))});

        SSS := '';

        for i := 1 to Length(SS) do
          SSS := SSS + '-';

        Add(SSS);
        Add('');
      end;

      Add(CurrentDateAndTime + ':');
      Add('  ' + S);
      Add('');

      SaveToFile('Application.log'));
      Free;
    end;
end;


In my program, I use it like this:

begin
  AddLogEntry('Program has started.');
...
end;
0
 
LVL 20

Expert Comment

by:Madshi
ID: 6939893
Please run the stuff inside of Delphi's IDE. In which line is the exception raised? For me it failed in the write routine. I don't like that assign too much, anyway, I'm generally using this logic instead:

procedure TMyStringList.SaveToFile(FileName: String);
const CLineFeed : string = #$D#$A;
var i1 : integer;
begin
  with TFileStream.Create(FileName, fmCreate) do
    try
      for i1 := 0 to List.Size - 1 do begin
        WriteBuffer(pointer(List.Items[i1])^, Length(List.Items[i1]));
        WriteBuffer(pointer(CLineFeed)^, 2);
      end;
    finally Free end;
end;

You should give in the whole file path (both when loading and saving), not only the file name. E.g., if you want to have the file stored in the same directory, where your exe is, you should use this:

  SaveToFile(ExtractFileName(ParamStr(0)) + 'Application.log');

Regards, Madshi.
0
 
LVL 20

Accepted Solution

by:
Madshi earned 50 total points
ID: 6939894
P.S: If you just want to write a log file without needing to delete lines you can use this little function, which I've written for my own bug tracing tests:

procedure log(fileName, str: string);
const CLineFeed : string = #$D#$A;
var c1, c2 : dword;
begin
  c1 := CreateFile(pchar(fileName), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_ALWAYS, 0, 0);
  if c1 <> INVALID_HANDLE_VALUE then begin
    SetFilePointer(c1, 0, nil, FILE_END);
    WriteFile(c1, pointer(str)^, length(str), c2, nil);
    WriteFile(c1, CLineFeed[1],  2,           c2, nil);
    CloseHandle(c1);
  end;
end;

This function is quite fast (and easy), because it doesn't read in the log file, it only appends one line to the end of the log file. Again: Please give in the whole file path, not only the file name.

Regards, Madshi.
0
 

Author Comment

by:DelFreak
ID: 6939900
What is this: const CLineFeed : string = #$D#$A;?
0
 

Author Comment

by:DelFreak
ID: 6939906
What is this: const CLineFeed : string = #$D#$A;?
0
 
LVL 20

Expert Comment

by:Madshi
ID: 6939923
Ehm, the name sais it all, does it not? Those are the two characters, that define a line feed in text files. The difference between a Write and a WriteLn is exactly those 2 characters... Just look at a text file in a hex editor, you'll again see those 2 characters...
0
 

Author Comment

by:DelFreak
ID: 6940021
Oh! I was kinda wondering if it was a #10#13. Anyway, I believe the error happens when I first start the app and it tries to do: AddLogEntry('Program has started.');

I'll try using your code though and see what happens.
0
 
LVL 20

Expert Comment

by:Madshi
ID: 6940068
>> Oh! I was kinda wondering if it was a #10#13.

It is, "$" is hexadecimal:

$D = 13
$A = 10
#$D = #13
#$A = #10

>> Anyway, I believe the error happens when I first start
the app and it tries to do: AddLogEntry('Program has started.');

Of course, but this AddLogEntry line calls the whole bunch of code you posted here, and somewhere in that code the real exception occurs, the big question is where exactly. You should see that if you run your program inside of the IDE.
0
 

Author Comment

by:DelFreak
ID: 6941565
Nevermind. Your "log" code works better and doesn't add much to my app. One more question though, if I want to use this code to create a file and read from it line by line, will that be possible? How can I do it?
0
 
LVL 20

Expert Comment

by:Madshi
ID: 6941574
The created file is nothing but a plain text file. So you can read it in by using TStringList.ReadFromFile or by using the LoadFromFile method that you posted in your question text.
0
 

Author Comment

by:DelFreak
ID: 6941687
Oh! Thanks!
0
 

Author Comment

by:DelFreak
ID: 6941689
Thank you! The code you supplied is excellent and just what I was hoping for.
0
 

Author Comment

by:DelFreak
ID: 6941709
Also, maybe you could help me here: http://www.experts-exchange.com/delphi/Q.20289518.html
0
 

Author Comment

by:DelFreak
ID: 6944148
Madshi, there is one other problem. How can I make it so that the log it creates looks like this:

Application Log - Generated on April 14, 2002
---------------------------------------------
Program started.


The first time the log is created, it writes the Application Log... and the -------------. Afterwards, it starts logging stuff.

Please help! I tried doing it but failed miserably. Thanks!
0
 

Author Comment

by:DelFreak
ID: 6957408
0

Featured Post

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
How to Install VMware Tools in Red Hat Enterprise Linux 6.4 (RHEL 6.4) Step-by-Step Tutorial

730 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question