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)
.LoadFromFile()
calls.LoadFromStream()
, which reads the whole stream/file into memory and then calls.SetTextStr()
, which just calls per line.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.