Solved

Dynamic Array problem. For Madshi or AvonWyss

Posted on 2002-04-11
22
221 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
  • 11
  • 7
  • 4
22 Comments
 
LVL 7

Expert Comment

by:God_Ares
Comment Utility
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
Comment Utility
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
Comment Utility
So TMyStringList should be = class and not = class(TObject)?
0
 
LVL 7

Expert Comment

by:God_Ares
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
Yeah, I do, really...   (-:
0
 

Author Comment

by:DelFreak
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
What is this: const CLineFeed : string = #$D#$A;?
0
Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

 

Author Comment

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

Expert Comment

by:Madshi
Comment Utility
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
Comment Utility
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
Comment Utility
>> 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
Comment Utility
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
Comment Utility
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
Comment Utility
Oh! Thanks!
0
 

Author Comment

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

Author Comment

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

Author Comment

by:DelFreak
Comment Utility
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
Comment Utility
0

Featured Post

6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
Firewall issues 6 85
Save pdf file to other location 3 72
oracle global variables 4 51
Find and Replace Stream with 0s 8 43
A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
Sending a Secure fax is easy with eFax Corporate (http://www.enterprise.efax.com). First, Just open a new email message.  In the To field, type your recipient's fax number @efaxsend.com. You can even send a secure international fax — just include t…
Illustrator's Shape Builder tool will let you combine shapes visually and interactively. This video shows the Mac version, but the tool works the same way in Windows. To follow along with this video, you can draw your own shapes or download the file…

743 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

Need Help in Real-Time?

Connect with top rated Experts

18 Experts available now in Live!

Get 1:1 Help Now