[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

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

load a file of dynamic record, do some calculations, output file

hi , i'm new to delphi
i want to load a file(i doesn't know the size of the record , and process the data inside, and then output it to a memo box.
i know how to read it into memory, how do i actually process the data inside the file,i need to do some average calculations.
e.g
Name   B    W    B
Joey    33   24   35
Hay     34   25   36

here the code  i use to read the file into memory

type TMeasurementRecord = record
  name : String[10];
  mark : array[1..3]of double;

end;

var
  myArray : array of TMeasurementRecord ;
  myFile : file of TMeasurementRecord ;

procedure TForm1.ButtonOpenFileClick(Sender: TObject);
begin
  if OpenDialog.Execute then
  begin
    AssignFile(myFile,OpenDialog.FileName);
    if FileExists(OpenDialog.FileName) then
    begin
      Reset(myFile);

      SetLength(myArray, 0);

      while not EOF (myFile) do
      begin
       
        SetLength(myArray, Length(myArray)+1);
        Read(myFile, myArray[Length(myArray)-1]);
      end;

     CloseFile(myFile);
    end
    else
      MessageDlg('The file '+ OpenDialog.FileName + ' does not exist',mtError,[mbOK],0);


  end;
0
blackdiamond_ks
Asked:
blackdiamond_ks
  • 7
  • 7
  • 3
2 Solutions
 
kretzschmarCommented:
i would prefer a tlist instead of an array looks like then


type
  TMeasurementRecord = record
    name : String[10];
    mark : array[1..3]of double;
  end;
  PMeasurementRecord = ^TMeasurementRecord;

var
  myList : TList;
  myFile : file of TMeasurementRecord ;
  p : PMeasurementRecord;

procedure TForm1.ButtonOpenFileClick(Sender: TObject);
begin
  if OpenDialog.Execute then
  begin
    MyList := TList.Create,
    AssignFile(myFile,OpenDialog.FileName);
    if FileExists(OpenDialog.FileName) then
    begin
      Reset(myFile);
     



      SetLength(myArray, 0);

      while not EOF (myFile) do
      begin
        p := new(PMeasurementRecord);
        read(myFile,p^);
        myList.Add(p);
      end;

     CloseFile(myFile);
    end
    else
      MessageDlg('The file '+ OpenDialog.FileName + ' does not exist',mtError,[mbOK],0);
  end;


just from head, typos possible

meikl ;-)
0
 
Wim ten BrinkCommented:
> SetLength(myArray, 0);
> while not EOF (myFile) do begin
>   SetLength(myArray, Length(myArray)+1);
>   Read(myFile, myArray[Length(myArray)-1]);
> end;

Why not:

  SetLength(myArray, FileSize(myFile));
  I := 0;
  while not EOF (myFile) do begin
    Read(myFile, myArray[I]);
    Inc(I);
  end;

The FileSize function tells you the number of records that exist in your file. So you create the list with the proper length right before you start reading. Delphi knows the size of the record with typed filetypes.

@meikl, personally I hate the TList type ever since Delphi added some good support for dynamic arrays. You might miss some functionality that the TList offers but those aren't hard to create anyway. Besides, the biggest advantage of the dynamic array is that you have TYPED records, not pointers that you need to convert and memory that you need to allocate all the time. (And free again.)
0
 
blackdiamond_ksAuthor Commented:
hi workshop how do i perform calculation and show it in memo box?

i assume the data is something like below

name B     W    B      Total
Andy 34.5 23.5 36            <-  how do i get the total of this person, and if there are 100 person , how do i get the total average of the 100 person
Bob   33   24.5  35
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.

 
kretzschmarCommented:
>@meikl, personally I hate the TList type ever since Delphi added some good support for
>dynamic arrays

yep thats true, i just like pointers :-))

>how do i get the total of this person, and if there are 100 person , how do i get the
>total average of the 100 person

result := myArray[Index].mark[1]+myArray[Index].mark[2]+myArray[Index].mark[3];

//average
average := 0;
mySum := 0;
for index := startindex to startindex+100 do //or +99
  mySum := mySum + myArray[Index].mark[1]+myArray[Index].mark[2]+myArray[Index].mark[3];
average := mySum/100;

again just from head

meikl ;-)
0
 
Wim ten BrinkCommented:
I'm not sure what this file actually contains. If the names can occur multiple times then you would need to calculate the average. But otherwise you could just write  myArray[Index].mark[1], myArray[Index].mark[2] and myArray[Index].mark[3].
0
 
blackdiamond_ksAuthor Commented:
hi kretzschmar, sorry i'm new to programming  , a

name B     W    B      Total
Andy 34.5 23.5 36            <-  if there are n person(i don't know how many persons are there , how do i get the total average of the  n person ??
Bob   33   24.5  35

result := myArray[Index].mark[1]+myArray[Index].mark[2]+myArray[Index].mark[3];      // so what the Index means ??

//average
average := 0;
mySum := 0;
for index := startindex to startindex+100 do     / / what's the startindex means??  
  mySum := mySum + myArray[Index].mark[1]+myArray[Index].mark[2]+myArray[Index].mark[3];
average := mySum/100;
0
 
kretzschmarCommented:
as you have an array

index means one entry from the array
sample index := 100 -> you may point to the 101th entry in the array (because zero-based array)

startindex defines in this case the start of a range
maybe you have 1000 entrys in your array, you could calculate
from the entry 52 up to 151th entry (i guess +99 is more correct to touch exactly 100 entries)

index and startindex are just placeholders in my sample

meikl ;-)
0
 
blackdiamond_ksAuthor Commented:
hi , in the form  i got  2 TButtons , 1 OpenDialog
the first button is  'OpenFile',  second is 'Save Output'.
After i opened a typed file, i wanna calculate the average of the marks and output it to a file
i got confused with the index thing
can  please look at the code

unit UnitLabTest2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Buttons, StdCtrls;

type
  TForm1 = class(TForm)
    ButtonOpenFile: TButton;
    BitBtn1: TBitBtn;
    OpenDialog: TOpenDialog;
    MemoOutput: TMemo;
    ButtonSaveOutput: TButton;
    procedure ButtonOpenFileClick(Sender: TObject);
    procedure ButtonSaveOutputClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//create a student record , i assume there are 2 fields in the file
type
  TStudentRecord = record
    name  : String[10];
    mark  : array[1..3]of double;
    //test1 : double;
    //test2 : double;
    //test3 : double;
end;

var
  StudentArray : array of TStudentRecord;
  StudentFile : file of TStudentRecord;

procedure TForm1.ButtonOpenFileClick(Sender: TObject);
var
  i :integer;
begin
  if OpenDialog.Execute then
  begin
    AssignFile(StudentFile,OpenDialog.FileName);
    if FileExists(OpenDialog.FileName) then
    begin
      //open the file for input
      Reset(StudentFile);

      // initialize the array
      //FileSize function tells you the number of records that exist in your file
      SetLength(StudentArray, FileSize(StudentFile));

      i := 0;
      while not EOF (StudentFile) do
      begin
        Read(StudentFile,StudentArray[i]);
        Inc(i);

        //SetLength(StudentArray, FileSize(StudentFile));

        //Add data to that space
        //Read(StudentFile, StudentArray[Length(StudentArray)-1]);
      end;

      //Finished working with the file , close it
      CloseFile(StudentFile);
    end
    else
      MessageDlg('The file '+ OpenDialog.FileName + ' does not exist',mtError,[mbOK],0);


  end;

end;

procedure TForm1.ButtonSaveOutputClick(Sender: TObject);
var
  result,average, mySum : double;
  index, i : integer;
begin
result := StudentArray[Index].mark[1]+StudentArray[Index].mark[2]+StudentArray[Index].mark[3];

//average
average := 0;
mySum := 0;
for index := 0 to Length(StudentArray) do //or +99
  mySum := mySum + StudentArray[Index].mark[1]+StudentArray[Index].mark[2]+StudentArray[Index].mark[3];
average := mySum/100;




end;

end.
0
 
Wim ten BrinkCommented:
Instead of using StartIndex, you could also use Low(myArray) and High(myArray) as lower and upper limits. By using

  for Index := Low(myArray) to High(myArray) do ...

you will actually walk through the whole list, allowing you to calculate an average, sum or whatever else. You can also use Length(myArray) if you need to know how many records your list actually contains. I tend to use these three methods with almost all my dynamic and static arrays.

Btw. I do wonder about the record structure that you are using. Are you absolutely sure that it is correct?
0
 
Wim ten BrinkCommented:
 mySum := 0;
  for index := Low(StudentArray) to High(StudentArray) do
    mySum := mySum + StudentArray[Index].mark[1]+StudentArray[Index].mark[2]+StudentArray[Index].mark[3];
  average := mySum/Length(StudentArray);
0
 
blackdiamond_ksAuthor Commented:
<< Btw. I do wonder about the record structure that you are using. Are you absolutely sure that it is correct?
i need to read a file with data field
name   Guy
test1  40.5
test2   35
test3   37.5

should i use

type
  TStudentRecord = record
    name  : String[10];
    test1 : double;
    test2 : double;
    test3 : double;
    //mark  : array[1..3]of double;


mySum := 0;
  for index := Low(StudentArray) to High(StudentArray) do            << so the index in this line is the same as the index at the next line???
    mySum := mySum + StudentArray[Index].mark[1]+StudentArray[Index].mark[2]+StudentArray[Index].mark[3];
  average := mySum/Length(StudentArray);

0
 
Wim ten BrinkCommented:
Actually, what I wondered about if you receive correct data in your application. Some typed files use a "packed record" instead of just a "record". But if the values are correctly read from the file, then the structure is correct.

And yes, the index used in the for loop is the same as the index in the next line. But when you get at the calculation of the average value, the index value might be invalid.

For an increase in performance, I often use:
  for index := High(StudentArray) downto Low(StudentArray) do ...
instead of that other for-loop. It saves a few clockticks so with small lists it doesn't save a lot. But when you have to walk through 10 million records, you do notice a bit speed increase. But this reverse loop is only safe to use if the order in which you process the data is unimportant.

The difference between:

  TStudentRecord = record
    name  : String[10];
    test1 : double;
    test2 : double;
    test3 : double;
  end;

and

  TStudentRecord = record
    name  : String[10];
    mark  : array[1..3]of double;
  end;

is just the names you gave to it. You could also have defined it as:

  TStudentRecord = record
    name  : String[10];
    mark  : array[65500..65502]of double;
  end;

as long as you refer to the fields in the proper way. Or you define this:

type
  TMarkType = (mtRed, mtYellow, mtGreen); // Three elements!
  TStudentRecord = record
    name  : String[10];
    mark  : array[TMarkType] of double;
  end;

in which case your loop would look like:

  mySum := 0;
  for index := Low(StudentArray) to High(StudentArray) do
    mySum := mySum + StudentArray[Index].mark[mtRed]+StudentArray[Index].mark[mtYellow]+StudentArray[Index].mark[mtGreen];
  average := mySum/Length(StudentArray);

Basically, it's just a matter of giving names to fields... If you know there are three fields next to each other, all of type Double, then you could use three different field names or just an array of 3 elements.
0
 
blackdiamond_ksAuthor Commented:
yah, now i understand that bit , and when i click on this button to calculate the total average of the mark
and access violation error occured

procedure TForm1.ButtonProcessClick(Sender: TObject);

var
  result,average, mySum : double;
  Index, i : integer;

begin
  Index := 0;            / /Should i put  Low(StudentArray)  ???
  result := StudentArray[Index].mark[1]+StudentArray[Index].mark[2]+StudentArray[Index].mark[3];         //this value was never used

  //average
  average := 0;
  mySum := 0;
  for i := Low(StudentArray) to High(StudentArray) do
    mySum := mySum + StudentArray[Index].mark[1]+StudentArray[Index].mark[2]+StudentArray[Index].mark[3];
    average := mySum/Length(StudentArray);


  MemoOutput.Lines.Clear;
  MemoOutput.Lines.Add('The average is ' + FloatToStr(average));       // i want to show the average but got error




end;
0
 
Wim ten BrinkCommented:
Well, did you fill that array correctly before you clicked the button? Check if Length(StudentArray) is larger than 0 before you start this routine. If it's not, the array is empty...

With dynamic arrays, Low(StudentArray) is always 0, btw. But I prefer to use Low() because it's a bit more readable.

Another minor flaw is in the for-loop...

  for i := Low(StudentArray) to High(StudentArray) do
    mySum := mySum + StudentArray[Index].mark[1]+StudentArray[Index].mark[2]+StudentArray[Index].mark[3];

Now you let variable i loop from low() to high() but you just add the record at position index. I think you wanted to use:

  for i := Low(StudentArray) to High(StudentArray) do
    mySum := mySum + StudentArray[i].mark[1]+StudentArray[i].mark[2]+StudentArray[i].mark[3];

instead. But to find the exact error, you will have to put a breakpoint at the first line of this procedure, run it in the debugger and click the button so Delphi stops at the beakpoint. From there, you have to step through your code, trying to find the location of the error. (Thus, at which point it crashes.)
0
 
blackdiamond_ksAuthor Commented:
hi , i'm almost finish the thing,
i calculated the average but it shows me 2.34242  (this doesn't matter)


var

  i : integer;
  average, mySum : double;

begin
  //i := 0;
  //result := StudentArray[i].mark[1]+StudentArray[i].mark[2]+StudentArray[i].mark[3];   << could u tell me what this line do ? cos i didn't put this line in my code

  //average
  average := 0;
  mySum := 0;
  for i := Low(StudentArray) to High(StudentArray) do
  begin
    mySum := mySum + StudentArray[i].mark[1]+StudentArray[i].mark[2]+StudentArray[i].mark[3];
    average := mySum/Length(StudentArray);
  end;

  MemoOutput.Lines.Clear;
  MemoOutput.Lines.Add('The average is ' + FloatToStr(average));

0
 
Wim ten BrinkCommented:
I see the error... You're calculating the average value within the loop. You need to calculate it OUTSIDE the loop! :-)

var
  i : integer;
  average, mySum : double;
begin
  //i := 0;
  //result := StudentArray[i].mark[1]+StudentArray[i].mark[2]+StudentArray[i].mark[3];   << could u tell me what this line do ? cos i didn't put this line in my code

  //average
  mySum := 0;
  for i := Low(StudentArray) to High(StudentArray) do
  begin
    mySum := mySum + StudentArray[i].mark[1]+StudentArray[i].mark[2]+StudentArray[i].mark[3];
  end;
  average := mySum/Length(StudentArray);
  MemoOutput.Lines.Clear;
  MemoOutput.Lines.Add('The average is ' + FloatToStr(average));

The value you got was just the sum of the last record divided by the number of records...
0
 
blackdiamond_ksAuthor Commented:
thank you guys,
0

Featured Post

Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

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