Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 234
  • Last Modified:

Dynamic Array problem. For Madshi or AvonWyss

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
DelFreak
Asked:
DelFreak
  • 11
  • 7
  • 4
1 Solution
 
God_AresCommented:
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
 
God_AresCommented:
if you are going to use your TMyStringList

do this

var mystinglist : TMyStringList ;

begin
  MyStringList := TMyStringList.create;
  ...
  ...
  TMyStringList.free;
end;
0
 
DelFreakAuthor Commented:
So TMyStringList should be = class and not = class(TObject)?
0
Independent Software Vendors: 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!

 
God_AresCommented:
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
 
MadshiCommented:
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
 
God_AresCommented:
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
 
MadshiCommented:
Yeah, I do, really...   (-:
0
 
DelFreakAuthor Commented:
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
 
MadshiCommented:
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
 
MadshiCommented:
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
 
DelFreakAuthor Commented:
What is this: const CLineFeed : string = #$D#$A;?
0
 
DelFreakAuthor Commented:
What is this: const CLineFeed : string = #$D#$A;?
0
 
MadshiCommented:
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
 
DelFreakAuthor Commented:
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
 
MadshiCommented:
>> 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
 
DelFreakAuthor Commented:
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
 
MadshiCommented:
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
 
DelFreakAuthor Commented:
Oh! Thanks!
0
 
DelFreakAuthor Commented:
Thank you! The code you supplied is excellent and just what I was hoping for.
0
 
DelFreakAuthor Commented:
Also, maybe you could help me here: http://www.experts-exchange.com/delphi/Q.20289518.html
0
 
DelFreakAuthor Commented:
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
 
DelFreakAuthor Commented:
0

Featured Post

New feature and membership benefit!

New feature! Upgrade and increase expert visibility of your issues with Priority Questions.

  • 11
  • 7
  • 4
Tackle projects and never again get stuck behind a technical roadblock.
Join Now