5
votes

While porting some code from Delphi 7 to Delphi 2010 I was rewriting my LoadTextFromFile() function.

function LoadTextFromFile(const aFullFileName: string): string;
var
  lBuffer:     TBytes;
  lEncoding:   TEncoding;
  lFileStream: TFileStream;
  lSize:       Integer;

begin

  if not FileExists(aFullFileName) then
  begin
    raise Exception.Create('File "' + aFullFileName + '" not found.');
  end;

  lFileStream := TFileStream.Create(aFullFileName, fmOpenRead + fmShareDenyNone);
  try

    if lFileStream.Size <= 0 then
    begin
      Result := '';
    end
    else
    begin

      lSize := lFileStream.Size - lFileStream.Position;

      SetLength(lBuffer, lSize);

      // Read file into TBytes buffer
      lFileStream.Read(lBuffer[0], lSize);

      // Read encoding from buffer
      TEncoding.GetBufferEncoding(lBuffer, lEncoding);

      // Get string from buffer
      Result := lEncoding.GetString(lBuffer);

    end;

  finally
    lFileStream.Free;
  end;

end;

When a thought was hitting my head: there must be something like this in the standard library. Many users want to read a text file into a string, but I could not find such a standard function. The closest I came was using TStringlist to load text. But A) creating a TStringlist looks unnecessary and B) I don't want to suffer the overhead from TStringlist.

Question: is there a standard LoadTextFromFile function in Delphi 2010?

4
Even in Delphi 7, you didn't have to do all that. Use a TStringStream instead of a TFileStream, and use Marco's answer. Also, I'm curious: Under what circumstances have you ever found that the Position property wasn't zero immediately after creating a stream, such that you would need to subtract it from Size in order to calculate lSize? - Rob Kennedy
Hi Rob, In Delphi 7 I had to be able to load/save to UTF8 files. And more importantly I did not know about TStringStream. The position does not need to be there. It is there because I copied some code out of the TStrings.Loadfromstream method that has the position in it. There is also another bug in my code as it does not handle files with BOM. So anyone thinking about using my example: don't. Use TFile.ReadAllText instead. - Jan Derk

4 Answers

14
votes

Yes exist one function like that in Delphi 2010, is called ReadAllText and is part of the TFile record declarated in the IOUtils unit.

Check this declaration

class function ReadAllText(const Path: string): string; overload; inline; static;
class function ReadAllText(const Path: string; const Encoding: TEncoding): string; overload; inline; static;

see this sample

program Project80;

{$APPTYPE CONSOLE}

uses
  IOUtils,
  SysUtils;
Var
 Content : string;
begin
  try
    Content:=TFile.ReadAllText('C:\Downloads\test.css'); //load the text of the file in a single line of code
    //Content:=TFile.ReadAllText('C:\Downloads\test.css',TEncoding.UTF8); //you can an encoding too.
    Writeln(Content);
    Readln;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.
4
votes

TStringStream.LoadFromStream/file ? Anyway very large strings are always a bit wasteful. You have to be very careful to not accidentally copy it.

2
votes

I tried the same solution, but made lEncoding a class variable to make it persistent, so I'd save my file to the same encoding as I read it. However, the call to GetBufferEncoding does nothing if lEncoding is already set to something (as it would be if a previous file was read). So, first I tried freeing it and that caused access violations all over the place. Now I just set it to nil before making the second call and it seems to work.

Something like this:

if Assigned(lEncoding) then begin
  lEncoding := nil;
end;
GetBufferedEncoding(buffer, lEncoding);
1
votes

A quick way is TStringList.LoadFromFile(). Then use TStringlist.Text to access the data as one big string. Don't forget to create/free, ideally using a try..finally block.