Clueless
Clueless

Reputation: 635

Reading a file in FreePascal

I have a text file with specified structure, namely (for each line): char, space, char, space, double value, endline. For instance

q w 1.23
e r 4.56
t y 7.89

What is the proper way to "extract" those values in Free Pascal?

Upvotes: 5

Views: 12455

Answers (3)

lukstafi
lukstafi

Reputation: 1895

Let's say that we are interested in reading a file, provided by a command line argument after a switch, containing substitution weights for characters.

program WeightFileRead;
uses SysUtils, StrUtils;
var
   MyFile : TextFile;
   FirstChar, SecondChar, DummyChar : Char;
   Weight : Double;
begin
   if GetCmdLineArg ('editweights', StdSwitchChars) = ''
   then begin
      WriteLn ('Syntax: WeightFileRead -editweights filename'); exit
   end;
   AssignFile (MyFile, GetCmdLineArg ('editweights', StdSwitchChars));
   Reset (MyFile);
   try
      while not EOF (MyFile) do
      begin
         ReadLn (MyFile, FirstChar, DummyChar, SecondChar, Weight);
         WriteLn ('A: ', FirstChar, '; B: ', SecondChar, '; W: ', Weight:0:1);
      end
   finally
      CloseFile (MyFile)
   end
end.

In a more general setting, when the first two entries can be longer strings, we can use ExtractWord that finds nth whitespace-separated word in a string, (or ExtractSubstr that treats several whitespaces together as introducing an empty word), and convert the third one into a number.

program WeightFileRead2;
uses SysUtils, StrUtils;
var
   MyFile : TextFile;
   FileLine : String;
begin
   if GetCmdLineArg ('editweights', StdSwitchChars) = ''
   then begin
      WriteLn ('Syntax: WeightFileRead -editweights filename'); exit
   end;
   AssignFile (MyFile, GetCmdLineArg ('editweights', StdSwitchChars));
   Reset (MyFile);
   try
      while not EOF (MyFile) do
      begin
         ReadLn (MyFile, FileLine);
         WriteLn ('A: ', ExtractWord (1, FileLine, [' ']),
                  '; B: ', ExtractWord (2, FileLine, [' ']),
                  '; W: ', StrToFloat (ExtractWord (3, FileLine, [' '])):0:1);
      end
   finally
      CloseFile (MyFile)
   end
end.

Note I use [' '] rather than StdWordDelims, since I don't want . or , to be a word delimiter.

Upvotes: 1

Wouter van Nifterick
Wouter van Nifterick

Reputation: 24086

FreePascal has the function SScanF in SysUtils (you might know if from other languages..)

I've modified RRUZ's example to show how to use it.

uses SysUtils;

type
  TData=object
    Val1 ,
    Val2 : String;
    Val3 : Double;
  end;

procedure ProcessFile(aFileName:String);
var
  F     : Text;
  LData : TData;
  Line  : String;
begin
  DecimalSeparator:='.';
  AssignFile(F,aFileName);
  Reset(F);
  while not eof(F) do
  begin
    ReadLn(F,Line);
    SScanf(Line,'%s %s %f',[@LData.Val1,@LData.Val2,@LData.Val3]);

    //do something with the data
    WriteLn(LData.Val1);
    WriteLn(LData.Val2);
    WriteLn(LData.Val3);
  end;
end;

begin
  ProcessFile('C:\Bar\Foo\Data.txt');
  Writeln('Press Enter to exit');
  Readln;
end.

Upvotes: 6

RRUZ
RRUZ

Reputation: 136391

You can use a TStringList class to load the file and the DelimitedText property to split the values on another TStringList and then store the values in a record.

Check this sample

{$mode objfpc}{$H+}

uses
  Classes, SysUtils;

{$R *.res}

type
  TData=record
    Val1: Char;
    Val2: Char;
    Val3: Double;
  end;

procedure ProcessFile;
var
  LFile  : TStringList;
  Line   : TStringList;
  i      : Integer;
  LData  : TData;
  LFormat: TFormatSettings;
begin
  //set the propert format for the foat values
  LFormat:=DefaultFormatSettings;
  LFormat.DecimalSeparator:='.';

  LFile:=TStringList.Create;
  Line :=TStringList.Create;
  try
   //load the file
   LFile.LoadFromFile('C:\Bar\Foo\Data.txt');
   Line.Delimiter:=' ';
    for i:=0 to LFile.Count-1 do
    begin
      //read the line and split the result
      Line.DelimitedText:=LFile[i];
      //some basic check
      if Line.Count  <> 3 then raise Exception.Create('Wrong data length');

      //you can add additional check here    
      LData.Val1:=Line[0][3]; 
      LData.Val2:=Line[1][4];
      LData.Val3:=StrToFloat(Line[2],LFormat);

      //do something with the data
      WriteLn(LData.Val1);
      WriteLn(LData.Val2);
      WriteLn(LData.Val3);
    end;
  finally
    Line.Free;
    LFile.Free;
  end;
end;

begin
 try
    ProcessFile;
 except on E:Exception do Writeln(E.Classname, ':', E.Message);
 end;
 Writeln('Press Enter to exit');
 Readln;
end.

Upvotes: 3

Related Questions