Textdateien zeilenweise einlesen

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Dets
Beiträge: 61
Registriert: Di 11. Sep 2007, 16:59
OS, Lazarus, FPC: Ubuntu Maverick (L 0.9.28.2-10, FPC 2.4.0)
CPU-Target: 32Bit
Wohnort: Lage
Kontaktdaten:

Textdateien zeilenweise einlesen

Beitrag von Dets »

Hi,
ich möchte Textdateien zeilenweise einlesen, ohne sie dazu komplett in den Speicher laden zu müssen (als TStringList). Dabei möchte ich aber wissen, welchen Fortschritt das Einlesen macht.

Unter Delphi habe ich das mit AssignFile, ReadLn und FilePos für TextFiles gemacht, aber Lazarus kennt kein FilePos für TextFiles. :(

Muss ich nun doch den Weg über eine StringList gehen, sollte ich besser einen Stream zeichenweise einlesen um daraus die Zeilen zusammenzubasteln oder gibt es noch eine elegantere Möglichkeit?

thx, Dets ...

pluto
Lazarusforum e. V.
Beiträge: 7192
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Beitrag von pluto »

Wenn ich dich richtig verstehe Zeit FilePos nur eine Position Angabe.

Wie list du denn Zeilweise ein ? in einer While schleife ?
Wenn ja, könntest du einfach eine Integer Variable hochzählen bei jeder neuen Zeile.

Ich hoffe du kannst mir folgen......
MFG
Michael Springwald

Dets
Beiträge: 61
Registriert: Di 11. Sep 2007, 16:59
OS, Lazarus, FPC: Ubuntu Maverick (L 0.9.28.2-10, FPC 2.4.0)
CPU-Target: 32Bit
Wohnort: Lage
Kontaktdaten:

Beitrag von Dets »

pluto hat geschrieben:Wenn ich dich richtig verstehe Zeit FilePos nur eine Position Angabe.
FilePos zeigt die bereits gelesen Bytes - wenn es diese Funktion in Lazarus für TextFiles gäbe!
pluto hat geschrieben:Wie list du denn Zeilweise ein ? in einer While schleife ?
Wenn ja, könntest du einfach eine Integer Variable hochzählen bei jeder neuen Zeile.
Das Einlesen geht mit

Code: Alles auswählen

repeat ... until EOF(f)
Ich könnte die Zeilen mitzählen, aber ich weiß ja nicht, wieviele Zeilen insgesamt vorhanden sind - also weiß ich nicht, wann die Verarbeitung abgeschlossen sein wird.

thx, Dets ...

pluto
Lazarusforum e. V.
Beiträge: 7192
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Beitrag von pluto »

doch das weis du: Meines wissen gibt ReadLn doch die gelesen Byte zurück richtig ?
Wenn ja, dann müsstest du nur noch prüfen ob ein Zeilenumbruch Zeichen drin, wenn ja nächste Zeile....

Nebenbei bemerkt: Du weiß doch so oder so wo er gerade ist, bei welcher Zeile oder ?
Also müsstest du es selbst einbauen.
MFG
Michael Springwald

Euklid
Lazarusforum e. V.
Beiträge: 2808
Registriert: Fr 22. Sep 2006, 10:38
OS, Lazarus, FPC: Lazarus v2.0.10, FPC 3.2.0
Wohnort: Hessen
Kontaktdaten:

Beitrag von Euklid »

Ja genau:

Du kannst die Dateien per AssignFile und anschließendem reset öffnen, um sie danach mit dem Befehl readln zeilenweise auszulesen.

Schau mal in der FPC-Dokumentation nach den Begriffen, hier müssten meines Wissens ein paar Beispiele sein.

Gruß, Euklid


EDIT:
Mit FPC-Dokumentation meine ich folgende Datei:
ftp://ftp.freepascal.org/pub/fpc/docs-pdf/rtl.pdf" onclick="window.open(this.href);return false;

Hier sind die genannten Begriffe erklärt und mit Beispielen untermauert.

_Bernd
Beiträge: 145
Registriert: Di 13. Feb 2007, 11:16

Re: Textdateien zeilenweise einlesen

Beitrag von _Bernd »

Dets hat geschrieben: Unter Delphi habe ich das mit AssignFile, ReadLn und FilePos für TextFiles gemacht, aber Lazarus kennt kein FilePos für TextFiles. :(
Zumindest mit Delphi 5 ging das auch nicht. Habe gerade nochmal in der Hilfe nachgeschaut.

Du könntest aber folgendes machen:

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
var
   t: System.Text;
   f: File;
   s: String;
   iFileSize: Integer;
   iReadBytes: Integer;
begin
   System.Assign(f, Edit1.Text);
   System.Reset(f, 1);
   iFileSize:= FileSize(f);
   System.Close(f);
   System.Assign(t, Edit1.Text);
   System.Reset(t);
   iReadBytes:= 0;
   while not EOF(t) do begin
      System.Readln(t, s);
      iReadBytes:= iReadBytes + System.Length(s) + 2; // zumindest bei Windows Text-Dateien
      ProgressBar1.Position:= (100 * iReadBytes) div iFileSize;
      Application.ProcessMessages;
   end;
   System.Close(t);
end;
Gruß, Bernd.

_Bernd
Beiträge: 145
Registriert: Di 13. Feb 2007, 11:16

Beitrag von _Bernd »

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
var
   t: System.Text;
   f: File;
   s: String;
   iFileSize: Integer;
   iReadBytes: Integer;
   iTextLineBreakSize: Integer;
begin
   System.Assign(f, Edit1.Text);
   System.Reset(f, 1);
   iFileSize:= FileSize(f);
   System.Close(f);
   System.Assign(t, Edit1.Text);
   System.Reset(t);
   iReadBytes:= 0;
   if DefaultTextLineBreakStyle = tlbsCRLF then
      iTextLineBreakSize:= 2
   else
      iTextLineBreakSize:= 1;
   while not EOF(t) do begin
      System.Readln(t, s);
      iReadBytes:= iReadBytes + System.Length(s) + iTextLineBreakSize;
      ProgressBar1.Position:= (100 * iReadBytes) div iFileSize;
      Application.ProcessMessages;
   end;
   System.Close(t);
end;
müßte sogar Crossplattform-konform sein ;-)

Gruß, Bernd.

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Textdateien zeilenweise einlesen

Beitrag von mschnell »

Dets hat geschrieben: ich möchte Textdateien zeilenweise einlesen, ohne sie dazu komplett in den Speicher laden zu müssen
readln macht das "out of the box", ist aber nicht up to date (objektorientiert).

Ich weiß nicht, ob es so as schon gibt, sonnst könntest Du dich im "Open Pascal Source"Bereich verdient machen.

Ein Objekt, das von TStrings abgeleitet ist und damit ein User-Interface ähnlich wie TStringList hat und Datei-Basiert arbeitet (zunächst wohl erst 'mal read-only).

Ich stelle mir vor, dass es mit einem TFileStream und einem cache-ähnlichem Puffer von einigern K Größe auf eine Datei zugreift und sich beim Einlesen (nicht schon beim create) eine Liste der Positionen der Zeilenumbrüche aufbaut. Lesen ist dann leicht zu machen. Auch "add" geht easy. Schreiben an eine bestimmte String-Position ist aufwändiger, da bei unterschiedlicher Länge die Datei umgeschichtet werden muss.

-Michael

pluto
Lazarusforum e. V.
Beiträge: 7192
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Beitrag von pluto »

genau sowas habe ich angefangen:
Ich habe eine eigene TStringList abgeleitet und alle notwendigen Methoden Überschrieben.
Dabei war das Ziel, das bei jedem lesen und schreiben von einem Eintrag ein Event ausgelöst wird. So sparst du Zeit.

Ich meine ich habe das so Geschrieben, das ich Windows und Linux Text Dateien laden konnte.
Wobei Speicher auch schon ging.


edit01: ich habe es mir eben noch einmal angeschaut, das ganze basiert auf TFileStream und das mit dem Event müsste noch eingebaut werden. Dürfte aber nicht schwer werden.
MFG
Michael Springwald

Dets
Beiträge: 61
Registriert: Di 11. Sep 2007, 16:59
OS, Lazarus, FPC: Ubuntu Maverick (L 0.9.28.2-10, FPC 2.4.0)
CPU-Target: 32Bit
Wohnort: Lage
Kontaktdaten:

Beitrag von Dets »

Danke euch allen. Bernd's Vorschlag ist der naheliegendste: einfach die Länge der von ReadLn zurückgelieferten Strings inklusive Zeilenende addieren. Ich weiß nicht, warum ich nicht selbst drauf gekommen bin :oops:

@Michael: Da ich sehr oft große Dateien einzulesen habe, habe ich mir einfache Klassen geschrieben, die Dateien zeilen- bzw. blockweise einlesen und entsprechende Events erzeugen.
Falls du/ihr mal einen Blick drauf werfen wollt ...

Dets ...

pluto
Lazarusforum e. V.
Beiträge: 7192
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Beitrag von pluto »

Es währe halt schön, wenn deine Klasse auch von TStrings abgeleitet würde, dann könnten wir sie z.b. bei TIniFile einsetzten oder anderen Klassen die ein TStrings Variante brauchen.....

Es ist auch (fast) keinen Aufwand.......
MFG
Michael Springwald

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Beitrag von mschnell »

pluto hat geschrieben:Es ist auch (fast) keinen Aufwand.......
Doch !!!
Überleg 'mal, was bei großen Dateien passiert ! (Kleine behandelt man eh besser mit TStringList.ReadFromFile.)

Bei Read klasse.string muss man entweder alle Zeilen vorher lesen, oder eine Art Cache und/oder Merker für die Zeilenumbrüche verwalten.

Wenn man die klasse verwendet:

Code: Alles auswählen

for i := 100000 to 100010 do begin
  memo1.add(klasse[i];
end;
dann ist die Methode ohne Cache/Merker wahnsinnig uneffektiv.

Und einen Cache und Merker zu programmieren ist schon eine ganze Menge Programmier-. und Test-Aufwand (aber eigentlich eine lohnende Aufgabe).

Beim Scheriben ist klasse.add kein Aufwand, aber klasse.strings := string ist heftig. Man kann natürlich direkt in der Datei rumwühlen und alle Bytes hinter der aktuellen Zeile verschieben. Das ist aber wahnsinnig uneffektiv. Ansonsten muss man den Cache und die (vermutlich eine TStringList) Datei asynchron verwalten und erst bei klasse.free und wenn der Cache zu groß wird die Datei umspeichern.

-Michael

Benutzeravatar
theo
Beiträge: 10869
Registriert: Mo 11. Sep 2006, 19:01

Beitrag von theo »

mschnell hat geschrieben: Und einen Cache und Merker zu programmieren ist schon eine ganze Menge Programmier-. und Test-Aufwand (aber eigentlich eine lohnende Aufgabe).
Keine schlechte Idee. Und wer macht die TFileStringList? Freiwillige vor! ;-)
mschnell hat geschrieben: Beim Scheriben ist klasse.add kein Aufwand, aber klasse.strings := string ist heftig. Man kann natürlich direkt in der Datei rumwühlen und alle Bytes hinter der aktuellen Zeile verschieben. Das ist aber wahnsinnig uneffektiv. Ansonsten muss man den Cache und die (vermutlich eine TStringList) Datei asynchron verwalten und erst bei klasse.free und wenn der Cache zu groß wird die Datei umspeichern.


Das sehe ich nicht ganz so. Gerade bei Riesenfiles (z.B. Logfiles) wird man sowieso meistens "adden". Insert und Delete dürften eher selten sein, und irgendwann muss man die sowieso ins File schreiben, also warum nicht gleich dann wenn's passiert?

pluto
Lazarusforum e. V.
Beiträge: 7192
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Beitrag von pluto »

Ich verstehe nicht das Problem: Es ging doch nur darum eine TStringList Variante zu haben die ein Ereignis beim Lesen und schreiben auslöst.

Und das ist wirklich kein Problem. Wobei ich habe im Soruce Code Gesehen, das eine Text Datei wie ein Stream geladen wird. So mache ich das bei mir auch im Moment. Das müsste ich natürlich anpassen.

Dann könnte noch ein Verschlüsslung und Komprimierung Funktionen eingebaut werden.

Es ging doch nicht darum wie man Große Dateien gut auslesen und schreiben kann. Das ist natürlich schon ein großer Aufwand. Ich habe da eine tolle Idee, evlt. setzte ich sie mal um, aber ich weiß noch nicht genau wann, erst muss ich noch einige Fragen klären.

@Theo und mschnell wie stellt ihr euch die TFileStringList? vor ?
MFG
Michael Springwald

Christian
Beiträge: 6079
Registriert: Do 21. Sep 2006, 07:51
OS, Lazarus, FPC: iWinux (L 1.x.xy FPC 2.y.z)
CPU-Target: AVR,ARM,x86(-64)
Wohnort: Dessau
Kontaktdaten:

Beitrag von Christian »

Nein es ging darum eine Stringlist zu haben die effizient in Dateien arbeitet und diese nicht einfach laden und speichern kann.
W.m.k.A.h.e.m.F.h. -> http://www.gidf.de/

Antworten