Home > Net >  use Delphi to read in a text file to a TStringList but bottom to top
use Delphi to read in a text file to a TStringList but bottom to top

Time:02-03

I was looking to read a text file in reverse so it would read in from the bottom of the text file first. I did find how to reverse it but it doesn't make sense to me. Can someone explain this to me, how it's working? Also if there is a better/quicker way? It seems to do all the work after the file is read in, like it would be quicker to just read it in from the bottom.

var
  datalist : TStringList;
  lines,i  : Integer;
  saveLine : String;
begin
  datalist := TStringList.Create;
  datalist.LoadFromFile(filename);   //loads file
  lines := datalist.Count;

  for i := lines-1 downto (lines div 2) do
    begin
      saveLine := datalist[lines-i-1];
      datalist[lines-i-1] := datalist[i];
      datalist[i] := saveLine;
    end;

CodePudding user response:

If you want to use the stringlist.loadfromfile function then another way to do it is to copy it to another stringlist. It would be faster than the current scheme and is fewer lines of code.

 var
      datalist1, datalist2 : tstringlist;
      lines,i: Integer;
      filename : string;
begin
  datalist1.LoadFromFile(filename);   //loads file
  lines := datalist1.Count;

   for i := lines-1 downto 0 do
   begin
     datalist2.Add (datalist1[i]);
   end;

end;

Personally, I would read the file in myself.

CodePudding user response:

(At least in Delphi 7, but more recent versions should act similarily)

  1. .LoadFromFile() calls
  2. .LoadFromStream(), which reads the whole stream/file into memory and then calls
  3. .SetTextStr(), which just calls per line
  4. .Add()

Knowing this helps us to avoiding to reinvent the whole wheel and instead using an own class with one subtle change in the .Add() method:

type
  TStringListReverse= class( TStringList )
    function Add( const S: String ): Integer; override;
  end;

function TStringListReverse.Add( const S: String ): Integer;
begin
  Result:= {GetCount} 0;  // Our change: always in front
  Insert( Result, S );
end;

And now we just use our own class:

var
  l: TStringListReverse;
begin
  l:= TStringListReverse.Create;
  l.LoadFromFile( 'C:\Windows\win.ini' );
  Memo1.Lines.Assign( l );
  l.Free;

CodePudding user response:

As I mentioned in a comment, it might be useful to create an adapter class that accepts a TStrings instance, and exposes it as another TStrings, but reversed.

This might look like this:

type
  TReversedStrings = class(TStrings)
  private
    FSource: TStrings;
    FOwnsSource: Boolean;
    function ReversedIndex(Index: Integer): Integer;
  protected
    procedure Put(Index: Integer; const S: string); override;
    function Get(Index: Integer): string; override;
    function GetCount: Integer; override;
    function GetObject(Index: Integer): TObject; override;
    procedure PutObject(Index: Integer; AObject: TObject); override;
  public
    constructor Create(Source: TStrings; AssumeOwnership: Boolean);
    destructor Destroy; override;
    procedure Clear; override;
    procedure Delete(Index: Integer); override;
    procedure Exchange(Index1, Index2: Integer); override;
    function IndexOf(const S: string): Integer; override;
    procedure Insert(Index: Integer; const S: string); override;
    procedure Move(CurIndex, NewIndex: Integer); override;
  end;

{ TReversedStrings }

constructor TReversedStrings.Create(Source: TStrings; AssumeOwnership: Boolean);
begin
  inherited Create;
  FSource := Source;
  FOwnsSource := AssumeOwnership;
end;

destructor TReversedStrings.Destroy;
begin
  if FOwnsSource then
    FSource.Free;
  inherited;
end;

function TReversedStrings.ReversedIndex(Index: Integer): Integer;
begin
  Result := FSource.Count - Index - 1;
end;

procedure TReversedStrings.Put(Index: Integer; const S: string);
begin
  FSource[ReversedIndex(Index)] := S;
end;

function TReversedStrings.Get(Index: Integer): string;
begin
  Result := FSource[ReversedIndex(Index)];
end;

function TReversedStrings.GetCount: Integer;
begin
  Result := FSource.Count;
end;

function TReversedStrings.GetObject(Index: Integer): TObject;
begin
  Result := FSource.Objects[ReversedIndex(Index)];
end;

procedure TReversedStrings.PutObject(Index: Integer; AObject: TObject);
begin
  FSource.Objects[ReversedIndex(Index)] := AObject;
end;

procedure TReversedStrings.Clear;
begin
  FSource.Clear;
end;

procedure TReversedStrings.Delete(Index: Integer);
begin
  FSource.Delete(ReversedIndex(Index));
end;

procedure TReversedStrings.Exchange(Index1, Index2: Integer);
begin
  FSource.Exchange(ReversedIndex(Index1), ReversedIndex(Index2));
end;

function TReversedStrings.IndexOf(const S: string): Integer;
begin
  Result := FSource.IndexOf(S);
  if Result > -1 then
    Result := ReversedIndex(Result);
end;

procedure TReversedStrings.Insert(Index: Integer; const S: string);
begin
  FSource.Insert(ReversedIndex(Index), S);
end;

procedure TReversedStrings.Move(CurIndex, NewIndex: Integer);
begin
  FSource.Move(ReversedIndex(CurIndex), ReversedIndex(NewIndex));
end;

It should be obvious how to use this, and I've not tested the code, or even executed it. Consider it a sketch of an idea.

  • Related