TFileStream, zum Öffnen sehr sehr großer Dateien..

Rund um die LCL und andere Komponenten

TFileStream, zum Öffnen sehr sehr großer Dateien..

Beitragvon corpsman » 25. Apr 2018, 08:39 TFileStream, zum Öffnen sehr sehr großer Dateien..

Servus,

ich versuche gerade auf einem Raspi mehrere Hundert MB große Textdateien zu verarbeiten.

Der Naive Ansatz:

Code: Alles auswählen
 
var sl:TStringlist;
i:integer;
begin
sl := TStringlist.create;
sl.loadfromfile(..);
for i := 0 to sl.clount -1 do begin
end;
sl.free
 


Komischerweise allokiert er für eine 160MB Textfile so viel Speicher, dass mir das 1GB-RAM eigentlich nicht mehr Reicht. Da mein Algorithmus im 1 Pass Verfahren einfach ein mal die Daten durchgeht war die Idee, nicht immer das gesamte File zu laden sondern "Blockweise" durch zu gehen.

Dazu wollte ich eigentlich TFiletream nehmen, aber laut : http://wiki.freepascal.org/TFileStream/de würde das nicht funktionieren, denn dort steht:

Bei einem Filestream wird eine Datei vollständig in den Arbeitsspeicher gelesen.


Meine Persönliche Erfahrung spiegelt das nicht wieder. Mit den Ntools versende ich zum Teil Gigabyte große Dateien und das geht wunderbar auch wenn die deutlich größer sind als der Speicher den der jeweilige Rechner gerade hat.

Ist hier das Wiki falsch, oder habe ich etwas übersehen.
Und Mit welcher Variante kann ich denn nun riesige Dateien stückweise durchgehen ?
--
Just try it
corpsman
 
Beiträge: 1037
Registriert: 28. Feb 2009, 08:54
Wohnort: Stuttgart
OS, Lazarus, FPC: Kubuntu 14.04, Lazarus SVN Trunk, FPC 3.0 | 
CPU-Target: 32Bit
Nach oben

Beitragvon wp_xyz » 25. Apr 2018, 09:01 Re: TFiletream, zum Öffnen sehr sehr Großer Dateien..

corpsman hat geschrieben:S
Dazu wollte ich eigentlich TFiletream nehmen, aber laut : http://wiki.freepascal.org/TFileStream/de würde das nicht funktionieren, denn dort steht:
Bei einem Filestream wird eine Datei vollständig in den Arbeitsspeicher gelesen.


Ich denke, da ist dem wiki-Autor die Phantasie durchgegangen. Abgesehen von der Pufferung im Betriebssystem, liest ein File-Stream so viele Bytes wie man im Read*-Befehl anfordert.
wp_xyz
 
Beiträge: 2460
Registriert: 8. Apr 2011, 08:01

Beitragvon corpsman » 25. Apr 2018, 09:04 Re: TFiletream, zum Öffnen sehr sehr Großer Dateien..

Gut, irritiert hatte es mich, da es in der Englischen variante auch steht.

Da ich ASCII files Zeilenweise verarbeite habe ich gemäß hier : http://forum.lazarus.freepascal.org/ind ... ic=16402.0

mal folgendes zusammengestrickt:

Code: Alles auswählen
 
Unit uunbufstringlist;
 
{$MODE objfpc}{$H+}
 
Interface
 
Uses
  Classes, SysUtils;
 
Type
 
  { TUnbufferedStringList }
 
  TUnbufferedStringList = Class
  private
    fFilename: String;
    ffile: TextFile;
    Procedure CloseFile;
  public
    Constructor Create;
    Destructor Destroy; override;
    Function LoadFromFile(Const Filename: String): Boolean;
 
    Function EOF: Boolean;
    Function Readln(): String;
  End;
 
Implementation
 
{ TUnbufferedStringList }
 
Constructor TUnbufferedStringList.Create;
Begin
  fFilename := '';
End;
 
Destructor TUnbufferedStringList.Destroy;
Begin
  CloseFile;
  Inherited Destroy;
End;
 
Procedure TUnbufferedStringList.CloseFile;
Begin
  If fFilename <> '' Then objpas.CloseFile(ffile);
End;
 
Function TUnbufferedStringList.LoadFromFile(Const Filename: String): Boolean;
Begin
  result := false;
  CloseFile;
  fFilename := '';
  If FileExists(Filename) Then Begin
    fFilename := Filename;
    assignfile(ffile, Filename);
    reset(ffile);
    result := true;
  End;
End;
 
Function TUnbufferedStringList.EOF: Boolean;
Begin
  result := true;
  If fFilename <> '' Then Begin
    result := system.EOF(ffile);
  End;
End;
 
Function TUnbufferedStringList.Readln(): String;
Begin
  result := '';
  If fFilename <> '' Then Begin
    system.Readln(ffile, result);
  End;
End;
 
End.
 


Damit muss ich recht wenige Anpassungen von TStringlist nach TUnbufferedStringList machen und müsste mein Problem lösen können ;)

Schreiben muss ich nicht. Sieht hier jemand Optimierungsbedarf ?
Zuletzt geändert von corpsman am 25. Apr 2018, 09:05, insgesamt 1-mal geändert.
--
Just try it
corpsman
 
Beiträge: 1037
Registriert: 28. Feb 2009, 08:54
Wohnort: Stuttgart
OS, Lazarus, FPC: Kubuntu 14.04, Lazarus SVN Trunk, FPC 3.0 | 
CPU-Target: 32Bit
Nach oben

Beitragvon Socke » 25. Apr 2018, 09:05 Re: TFiletream, zum Öffnen sehr sehr Großer Dateien..

Versuch mal:
Code: Alles auswählen
var
  sl: TStringList;
  fs: TFileStream;
  ss: TStringStream;
  i: Integer;
begin
  fs := TFileStream.Create('myfile.txt', fmOpenRead or fmShareDenyWrite);
  ss := TStringStream.Create('');
  sl := TStringList.Create;
  try
    ss.Size := fs.Size;
    ss.CopyFrom(fs, fs.Size);
    ss.Position := 0;
    sl.Text := ss.DataString;
    // daten verarbeiten
    for i := 0 to sl.clount -1 do begin
      doSomething()i;
    end;
  finally
    ss.Destroy;
    fs.Destroy;
    sl.Destroy;
  end;


TFileStream hat keinen eigenen Puffer. Problematisch ist TStrings.LoadFromStream(), das für normale Dateien den Buffer ineffizient in einer Schleife allokiert (wahrscheinlich um non-seekable Streams wie Pipes zu unterstützen).
Mit dem Ansatz oben hast brauchst du zeitweise den doppelten Speicher für eine Datei (im TStringStream und in der TStringList). Alternativ hilft hier nur TStrings.LoadFromStream() neu zu implementieren, dabe die Datei stückweise einzulesen und den Teil des Buffers bis zum letzten Zeilenumbruch mit TStrings.AddText() hinzuzufügen.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein
Socke
 
Beiträge: 2484
Registriert: 22. Jul 2008, 18:27
Wohnort: Köln
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 8.1/Debian GNU/Linux/Raspbian/openSUSE | 
CPU-Target: 32bit x86 armhf
Nach oben

Beitragvon corpsman » 25. Apr 2018, 09:19 Re: TFiletream, zum Öffnen sehr sehr Großer Dateien..

@socke

Deine Variante würde also auf meinem 1GB raspi Dateien bis 500MB zulassen (zumindest in der Theorie).

Mit meinem Vorschlag aus dem vorherigen Post, müsste die Dateigröße durch das Filesystem begrenzt sein, oder ?
--
Just try it
corpsman
 
Beiträge: 1037
Registriert: 28. Feb 2009, 08:54
Wohnort: Stuttgart
OS, Lazarus, FPC: Kubuntu 14.04, Lazarus SVN Trunk, FPC 3.0 | 
CPU-Target: 32Bit
Nach oben

Beitragvon kralle » 25. Apr 2018, 09:37 Re: TFiletream, zum Öffnen sehr sehr Großer Dateien..

Moin,

corpsman hat geschrieben:Servus,
ich versuche gerade auf einem Raspi mehrere Hundert MB große Textdateien zu verarbeiten.


Was willst Du nach den Einlesen mit dem Text machen?
Wenn Du den Text mit
Code: Alles auswählen
READLN(f,zeile)
zeilenweise einliest, brauchst Du echt wenig Speicher.

Gruß Heiko
Linux Mint 18.3 und Lazarus 1.8.2 (FPC-Version: 3.0.4)
Windows 8.1 Pro Lazarus 1.8.2 + Delphi XE7SP1
kralle
 
Beiträge: 510
Registriert: 17. Mär 2010, 14:50
Wohnort: Bremerhaven
OS, Lazarus, FPC: Linux Mint 18.3 - Lazarus 1.9 - FPC 3.0.4 -Win8.1 & XE7Pro | 
CPU-Target: 64Bit
Nach oben

Beitragvon corpsman » 25. Apr 2018, 09:40 Re: TFiletream, zum Öffnen sehr sehr Großer Dateien..

Ich sende den Text via Uart an einen 3D-Drucker ;)
--
Just try it
corpsman
 
Beiträge: 1037
Registriert: 28. Feb 2009, 08:54
Wohnort: Stuttgart
OS, Lazarus, FPC: Kubuntu 14.04, Lazarus SVN Trunk, FPC 3.0 | 
CPU-Target: 32Bit
Nach oben

Beitragvon Socke » 25. Apr 2018, 09:44 Re: TFiletream, zum Öffnen sehr sehr Großer Dateien..

corpsman hat geschrieben:Deine Variante würde also auf meinem 1GB raspi Dateien bis 500MB zulassen (zumindest in der Theorie).

Mit virtuellem Speicher kommst du noch weiter - die Frage ist: willst du dir eine 128 GB SD-Karte anschaffen um 30 GB große Dateien einzulesen? Die nutzbare Grenze hängt von vielen Faktoren ab, ohne grafische Oberfläche vermutlich bei einer Dateigröße von ca. 300 MB (300 MB Stringstream + 300 MB Stringlist + fester Videospeicher + restliches System)

corpsman hat geschrieben:Mit meinem Vorschlag aus dem vorherigen Post, müsste die Dateigröße durch das Filesystem begrenzt sein, oder ?

Hier liest du nur Zeilenweise ein; sofern du nicht alles wieder in eine Stringlist lädst oder anderweitig im Speicher behälst kannst du damit beliebig große Dateien verarbeiten.
Wenn du längere Zeilen hast, solltest du einen eigenen Puffer definieren und mit SetTextBuf() setzen.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein
Socke
 
Beiträge: 2484
Registriert: 22. Jul 2008, 18:27
Wohnort: Köln
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 8.1/Debian GNU/Linux/Raspbian/openSUSE | 
CPU-Target: 32bit x86 armhf
Nach oben

Beitragvon corpsman » 25. Apr 2018, 09:50 Re: TFiletream, zum Öffnen sehr sehr Großer Dateien..

@socke

da ich GCode files vesende müssten mir die default 128 reichen.

Wie ich gerade feststelle lese ich dei Datei aber doch im 2 pass.

Reicht es wenn ich nach dem Ersten Pass ein "reset(file)" mache oder muss ich die Datei schließen und neu öffnen ?
--
Just try it
corpsman
 
Beiträge: 1037
Registriert: 28. Feb 2009, 08:54
Wohnort: Stuttgart
OS, Lazarus, FPC: Kubuntu 14.04, Lazarus SVN Trunk, FPC 3.0 | 
CPU-Target: 32Bit
Nach oben

Beitragvon kralle » 25. Apr 2018, 10:05 Re: TFiletream, zum Öffnen sehr sehr Großer Dateien..

Moin,

corpsman hat geschrieben:Ich sende den Text via Uart an einen 3D-Drucker ;)

Wenn jede Zeile ein Kommando für den Drucker enthält, was spricht dann gegen Zeilenweises Einlesen und an den Drucker senden?
Oder wäre das zu langsam, so das der Drucker Probleme damit hätte?

Gruß Heiko
Linux Mint 18.3 und Lazarus 1.8.2 (FPC-Version: 3.0.4)
Windows 8.1 Pro Lazarus 1.8.2 + Delphi XE7SP1
kralle
 
Beiträge: 510
Registriert: 17. Mär 2010, 14:50
Wohnort: Bremerhaven
OS, Lazarus, FPC: Linux Mint 18.3 - Lazarus 1.9 - FPC 3.0.4 -Win8.1 & XE7Pro | 
CPU-Target: 64Bit
Nach oben

Beitragvon corpsman » 25. Apr 2018, 10:10 Re: TFiletream, zum Öffnen sehr sehr Großer Dateien..

Das mache ich im Prinzip auch so

Code: Alles auswählen
 
// Open File
// 1. Pas = Count all Commands
// reset File
// 2. Pas = Sende all Command + Aktualisiere den Fortschritsbalken
 


Problem, Anzahl Commands <> sl.count, deswegen muss ich im 1. Pass erst die Anzahl der tatsächlichen Befehle Berechnen.

[Edit]

Jetzt hab ichs geblickt, du meinst die File/IO auf der SD-Karte des Pi's.

Die war bisher ja kein Problem, da ich ja alles im Ram hatte, bin mal gespannt ob es jetzt zu einem Problem wird.
--
Just try it
corpsman
 
Beiträge: 1037
Registriert: 28. Feb 2009, 08:54
Wohnort: Stuttgart
OS, Lazarus, FPC: Kubuntu 14.04, Lazarus SVN Trunk, FPC 3.0 | 
CPU-Target: 32Bit
Nach oben

Beitragvon Socke » 25. Apr 2018, 10:22 Re: TFiletream, zum Öffnen sehr sehr Großer Dateien..

corpsman hat geschrieben:Reicht es wenn ich nach dem Ersten Pass ein "reset(file)" mache oder muss ich die Datei schließen und neu öffnen ?

Ich denke, reset(file) reicht aus. Ich musste gerade feststellen, dass es hier keine Möglichkeit gibt eine bestimmte Position anzuspringen.

corpsman hat geschrieben:Die war bisher ja kein Problem, da ich ja alles im Ram hatte, bin mal gespannt ob es jetzt zu einem Problem wird.

Wenn du den Aufwand treiben willst, erstellst du einen Lese-Thread mit Read Ahead und einen Sende-Thread :D
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein
Socke
 
Beiträge: 2484
Registriert: 22. Jul 2008, 18:27
Wohnort: Köln
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 8.1/Debian GNU/Linux/Raspbian/openSUSE | 
CPU-Target: 32bit x86 armhf
Nach oben

Beitragvon corpsman » 25. Apr 2018, 10:27 Re: TFiletream, zum Öffnen sehr sehr Großer Dateien..

*g*

ich habe schon einen eigenen Thread der das Senden macht.

Mein Raspi läuft hier im Consolenmodus und hat als Display ein C-Berry Display, eine Network Connection gibt es nicht -> Eigentlich müsste dem tierisch langweilig sein.

Da außer über die GPIO's er nirgens "beschäftigt" werden kann und die GPIO's fragt der Hauptthread ab.

Habe nun mal gemäß hier: https://www.raspberrypi.org/forums/view ... p?t=114228

die Textbuf Größe auf 4KB gesetzt, es wird sich in der Praxis zeigen ob ich den Extra Thread für den LookAhead brauche oder nicht..

Aktuelle Version:
Code: Alles auswählen
 
Unit uunbufstringlist;
 
{$MODE objfpc}{$H+}
 
Interface
 
Uses
  Classes, SysUtils;
 
Type
 
  { TUnbufferedStringList }
 
  TUnbufferedStringList = Class
  private
    IOBuffer: Array[0..(1024 * 4) - 1] Of byte; //  Der Raspi scheint eine 4KB Paging buffer size zu haben ..
    fFilename: String;
    ffile: TextFile;
    Procedure CloseFile;
  public
    Constructor Create;
    Destructor Destroy; override;
    (*
     * Datei wird beim erneuten Laden oder .free Automatisch geschlossen
     *)

    Function LoadFromFile(Const Filename: String): Boolean;
 
    (*
     * Zugriff auf die Daten..
     *)

    Procedure Reset();
    Function EOF: Boolean;
    Function Readln(): String;
  End;
 
Implementation
 
{ TUnbufferedStringList }
 
Constructor TUnbufferedStringList.Create;
Begin
  fFilename := '';
End;
 
Destructor TUnbufferedStringList.Destroy;
Begin
  CloseFile;
  Inherited Destroy;
End;
 
Procedure TUnbufferedStringList.CloseFile;
Begin
  If fFilename <> '' Then objpas.CloseFile(ffile);
End;
 
Function TUnbufferedStringList.LoadFromFile(Const Filename: String): Boolean;
Begin
  result := false;
  CloseFile;
  fFilename := '';
  If FileExists(Filename) Then Begin
    fFilename := Filename;
    assignfile(ffile, Filename);
    system.reset(ffile);
    system.SetTextBuf(ffile, IOBuffer);
    result := true;
  End;
End;
 
Procedure TUnbufferedStringList.Reset();
Begin
  system.Reset(ffile);
End;
 
Function TUnbufferedStringList.EOF: Boolean;
Begin
  result := true;
  If fFilename <> '' Then Begin
    result := system.EOF(ffile);
  End;
End;
 
Function TUnbufferedStringList.Readln(): String;
Begin
  result := '';
  If fFilename <> '' Then Begin
    system.Readln(ffile, result);
  End;
End;
 
End.
 
 
--
Just try it
corpsman
 
Beiträge: 1037
Registriert: 28. Feb 2009, 08:54
Wohnort: Stuttgart
OS, Lazarus, FPC: Kubuntu 14.04, Lazarus SVN Trunk, FPC 3.0 | 
CPU-Target: 32Bit
Nach oben

• Themenende •

Zurück zu Komponenten und Packages



Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 1 Gast

porpoises-institution
accuracy-worried