Search code examples
variablestypespascalreadline

Pascal ReadLn multiple var types


My problem looks like this. I got a file with data(input) "text text number number" on each line, I need to get data from it using ReadLn(myfile, string, string2, real1(or double), real2(or double));, as you can see in my code provided below, but when I do it with the function I've just described, I get an error. I need each data in separate variable. I need to work with numbers(multiply, sum) and I need name and surname for the output(result) file, where data will look like this "Name Surname number1(from input file) multiplied with number2(from input file)" for each line from the input file and in the last line will be "Total: sum(all lines(number1*number2))". The only problem I need help with is to read each line data from initial file to a separate variable.

File explanation:

  • Name Surname NumericValue(double type) NumericValue(double type)

    E.g.:Somedude Hissurname 86.5 9.85

I always get an error while trying to read the line data like this:

  • ReadLn(filevar(:text), name(:array of string[25]), surname(:array of string[25]), numeric1(:real), numeric2(:real));

    E.g.:ReadLn(data, name[], sur[], meters, price);

When I try same thing, but after changing numeric values to integer(in file as well):

Changes done in the file:

  • Name Surname NumericValue(integer type) NumericValue(integer type)

    E.g. Somedude Hissurname 86 9

And program code looks like this:

  • ReadLn(filevar(:text), name(:array of string[25]), surname(:array of string[25]), numeric1(:integer), numeric2(:integer));

    E.g.:ReadLn(data, name[], sur[], meters, price);

Program works almost as intended(numeric1 takes both numeric values and 2nd is left blank).

E.g.: name[] = Somedude, sur[] = Hissurname, numeric1 = 86 9, numeric2 = 0

Here's how far I got with my code:

program test;
type    mas = array[1..30] of real;
        mas1 = array[1..30] of string[25];

var name,sur:mas1;
    atl:mas;
    meters,price,total:integer;
    i,p,n       :integer;
    data      :text;
begin
    Assign(data, 'somefile.txt');
    Reset(data);
    ReadLn(data,n);
    for i:=1 to n do
        begin
                  ReadLn(data, name[i], sur[i], meters, price);
                  WriteLn(name[i],meters, price);
        end;
    Close(data);
    ReadLn;
end.

Thank you in advance. I've did similar post earlier, but the solution doesn't suite school needs(too complicated for them -.-) I need to use simple way of ReadLn with 4 variables in it(Or 2 or 3, if I'm wrong). In any case, forgive me for posting same problem twice.

P.S. Forgive me for formatting, spelling and idea issues. I was rather sleepy. I hope I made myself clear this time.


Solution

  • (Answering to the first paragraph for the reason Ken stated in comments to the question)

    According to the standard you should be able to do this. The ReadLn procedure can take a variable number of arguments (which only would make sense if you're able to read values into those arguments).

    With two pascal compilers I can test on, this line

    ReadLn(f, s1, s2, r1, r2);
    

    compiles fine (where 'f' is a TextFile, 's1' and 's2' are strings and 'r1' and 'r2' are Reals).

    To make it work is a bit difficult though. If you provide the below input (taken from your previous question),

    Brat Sunbather 5.66 55.4

    Delphi will fail with 'Invalid numeric input (EInOutError)', and FPC will fail with a 'Invalid Input (EInOutError)'. The problem is with the string variables that we want to read into. The whole line we provide is in fact a string (a space is a perfectly valid character in a string), so when the code sees a string as the first variable to read into, it reads the entire line to it.

    Hence, the solution is to provide a special character that would mark the end of a string. A Ctrl+Z (ASC dec:26 hex:1A) works for Delphi (I don't know what would (if anything) work for FPC or other compilers). So the specially crafted text file (for Delphi) is like:

    2
    Brat Sunbather 5.66 55.4
        ↑         ↑    ↑    ↑
       ^Z        ^Z space  eol
    Bart Simpson 55.7 45.4 
        ↑       ↑    ↑    ↑
       ^Z      ^Z space  eol
    


    edit: If you have no control over how the input file is produced, and you're certain that a 'space' is a delimiter to a string variable, you can read the string variables character by character in a loop until you read a space. An example can be something like this:

    procedure TForm1.Button1Click(Sender: TObject);
    var
      f: TextFile;
      fn: string;
    
      Lines: Integer;
      i: Integer;
      first, last: string;
      R1, R2, Total: Real;
    
      function ReadName: string;
      var
        c: Char;
      begin
        Result := '';
        repeat
          Read(f, c);
          Result := Result + c;
        until c = ' ';
      end;
    
    begin
      fn := 'data.txt';
      AssignFile(f, fn);
      Reset(f);
      Read(f, Lines);
      Readln(f);
      Total := 0;
      for i := 1 to Lines do begin
        first := ReadName;
        last := ReadName;
        Read(f, R1, R2);
    
    // do whatever necessary with names & numbers
    //    showmessage(first + ' ' + last + ' - ' + FloatToStr(R1 * R2));
    
        Total := Total + R1 * R2;
        Readln(f);
      end;
    // do whatever necessary with Total
    //  ShowMessage(FloatToStr(Total));
    
      CloseFile(f);
    end;