[2 days left] What’s wrong with your cloud strategy? Learn why multicloud solutions matter with Nimble Storage.Register Now

x
?
Solved

Dynamic Array problem. For Madshi or AvonWyss

Posted on 2002-04-11
22
Medium Priority
?
232 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
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 
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 200 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

Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

Question has a verified solution.

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

Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
In this video, Percona Solution Engineer Dimitri Vanoverbeke discusses why you want to use at least three nodes in a database cluster. To discuss how Percona Consulting can help with your design and architecture needs for your database and infras…
Please read the paragraph below before following the instructions in the video — there are important caveats in the paragraph that I did not mention in the video. If your PaperPort 12 or PaperPort 14 is failing to start, or crashing, or hanging, …
Suggested Courses

649 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