Sauberes Filehandling

Für Fragen von Einsteigern und Programmieranfängern...
Uli
Beiträge: 24
Registriert: Mi 2. Sep 2009, 18:49

Sauberes Filehandling

Beitrag von Uli »

Hallo,
häufig sehe ich folgenden Code im Internet

try
try
assign(Txt, 'Filename');
rewrite(Txt);
writeln(Txt, 'test');
except
on E: Exception do writeln('Exception: ', E.Message);
end;
finally
close(Txt);
end;

Dabei kann aber auch close(Txt) eine Exception auslösen.
Wenn ich aber close(Txt) mit in den try Block nehme, habe ich das nächste Problem, ich weiß im Exception Block nicht, ob die Datei schon geschlossen ist. Da es keine Funktion gibt die mir sagt ob die Datei offen oder geschlossen ist, muss ich mir das in der Variablen isFileOpen selber merken. Dabei kommt dann folgender Code Heraus.

try
assign(Txt, 'f:\Prog\FreePascal\test3\test.txt');
isFileOpen := false;
rewrite(Txt);
isFileOpen := true;
writeln(Txt, 'test');
isFileOpen := false;
close(Txt);
except
on E: Exception do begin
if isFileOpen then begin
{$I-} close(Txt); {$I+}
if ioresult = 0 then;
end;
writeln('Exception: ', E.Message);
end;
end;

Und jetzt meine Frage, geht das wirklich nicht einfacher????

Vielen Dank für jede Antwort.

Scotty
Beiträge: 768
Registriert: Mo 4. Mai 2009, 13:24
OS, Lazarus, FPC: Arch Linux, Lazarus 1.3 r44426M FPC 2.6.4
CPU-Target: x86_64-linux-qt/gtk2
Kontaktdaten:

Re: Sauberes Filehandling

Beitrag von Scotty »

Und jetzt meine Frage, geht das wirklich nicht einfacher????
Na klar: TFileStream oder bei reinem Text: TStringList.SaveToFile. Zum Beispiel:

Code: Alles auswählen

sl:=TStringList.Create;
try
  sl.Add('Test');
  sl.SaveToFile('xyz.txt');
finally
  sl.Free:
end;

Das klassische Filehandling hat IMHO gar keine Exceptions - Fehler stehen im IOResult. Ansonsten solltest du Code formatieren (->Highlighter), das liest sich leichter :mrgreen:

Socke
Lazarusforum e. V.
Beiträge: 3158
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Sauberes Filehandling

Beitrag von Socke »

Scotty hat geschrieben:Na klar: TFileStream oder bei reinem Text: TStringList.SaveToFile. Zum Beispiel:

Code: Alles auswählen

sl:=TStringList.Create;
try
  sl.Add('Test');
  sl.SaveToFile('xyz.txt');
finally
  sl.Free:
end;

Das hat dann aber nicht mehr viel mit Filehandling zu tun, da du eine Wrapper-Klasse verwendest. Willst du die Programmlogik einfach gestalten ohne unnötige Klassen, solltest du mal einen Blick auf die API-Wrapper legen. Die abstrahieren nur den API-Aufruf und verwenden deshalb auch die OS-Filehandles. Die Pascalfunktionen basteln da ja noch ein bisschen mehr drumherum.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Uli
Beiträge: 24
Registriert: Mi 2. Sep 2009, 18:49

Re: Sauberes Filehandling

Beitrag von Uli »

Danke für die Antworten,
werde mir beides mal genauer anschauen.

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

Re: Sauberes Filehandling

Beitrag von theo »

Socke hat geschrieben:ohne unnötige Klassen


Dieses Argument kann ich immer nicht verstehen.
TFileStream oder TStringList machen das doch bestens und fehlerunanfällig.
Warum immer alles neu erfinden? Mit dieser Logik brauchst du die ganze LCL nicht.

marcov
Beiträge: 1100
Registriert: Di 5. Aug 2008, 09:37
OS, Lazarus, FPC: Windows ,Linux,FreeBSD,Dos (L trunk FPC trunk)
CPU-Target: 32/64,PPC(+64), ARM
Wohnort: Eindhoven (Niederlande)

Re: Sauberes Filehandling

Beitrag von marcov »

alles unter $I-, und mit IOResult Testen?

Socke
Lazarusforum e. V.
Beiträge: 3158
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Sauberes Filehandling

Beitrag von Socke »

theo hat geschrieben:
Socke hat geschrieben:ohne unnötige Klassen


Dieses Argument kann ich immer nicht verstehen.
TFileStream oder TStringList machen das doch bestens und fehlerunanfällig.
Warum immer alles neu erfinden? Mit dieser Logik brauchst du die ganze LCL nicht.

Achtung: TFileStream udn TStringList gehören zu FCL nicht zur LCL!

Die LCL ist meines Erachtens sinnvoll, da sie die verschiedenen Widgetsets abstrahiert (auch wenn ein wxWidgets-Pascal-Layer das ganze auch gemacht hätte ohne das Rad neu zu erfinden).
Die FCL enthält viele Klassen, die vor allem zur Wiederverwertung und Bequemlichkeit gedacht sind. Wenn man ein sehr schnelles Programm brauch, kann man sein Ziel ohne diese Klassen sehr viel einfacher erreichen, auch wenn die Komplexität des Quellcodes zunimmt.
Speziell auf TFileStream bezogen ergeben sich mehrere Abstraktionsschichten. An erster Stelle steht das Betriebssystem und dessen API, danach kommt die RTL, die für alle Betriebssysteme die gleiche Schnittstelle zur Verfügung stellt. Danach kommt die TFileStream, wodurch die RTL-Funktionen als TStream objektorientiert gekapselt werden.
Jede Schicht fügt weiteren Overhead hinzu, der die Geschwindigkeit drosselt.

In der Praxis werden sich die Geschwindigkeitsunterschiede zwar kaum auswirken - das ist dann aber auf die Prozessorgeschwindigkeit und nicht auf die Funktionen zurückzuführen. Bei Java oder .NET ist es ähnlich. Mittlerweile kann mit dieser Technologie eine ähnliche Geschwindigkeit wie bei nativen Programmen erreicht werden. In diesem Falle liegts teilweise an der JIT-Complierung. Verfrachtet man die neuste Java-Version aber auf einen alten 800 Mhz Prozessor, wird dort die Bytecode-Interpretation (!) recht langsam ablaufen.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

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

Re: Sauberes Filehandling

Beitrag von theo »

Socke hat geschrieben:Achtung: TFileStream udn TStringList gehören zu FCL nicht zur LCL!

Falsch, die gehören zur RTL. Deshalb habe ich auch die LCL als Beispiel genommen, weil ohne RTL eh nicht viel läuft.

Socke hat geschrieben:Jede Schicht fügt weiteren Overhead hinzu, der die Geschwindigkeit drosselt.
...
In der Praxis werden sich die Geschwindigkeitsunterschiede zwar kaum auswirken

Beides Richtig. Aber wieso muss man das dann jedem Newbie an den Kopf werfen?
Er soll doch möglichst einfach vorankommen, und die Nutzung der Klassen lernen.

Den Rest kann man ihm dann erklären, wenn er ein spezielles Problem hat, welches er mit den Klassen nicht lösen kann, was in der Praxis selten vorkommen dürfte.

Socke
Lazarusforum e. V.
Beiträge: 3158
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Sauberes Filehandling

Beitrag von Socke »

theo hat geschrieben:Falsch, die gehören zur RTL. Deshalb habe ich auch die LCL als Beispiel genommen, weil ohne RTL eh nicht viel läuft.

Auch möglich, habe nicht nachgesehen, tut aber auch nichts zur Sache.
theo hat geschrieben:
Socke hat geschrieben:Jede Schicht fügt weiteren Overhead hinzu, der die Geschwindigkeit drosselt.
...
In der Praxis werden sich die Geschwindigkeitsunterschiede zwar kaum auswirken

Beides Richtig. Aber wieso muss man das dann jedem Newbie an den Kopf werfen?
Er soll doch möglichst einfach vorankommen, und die Nutzung der Klassen lernen.

Den Rest kann man ihm dann erklären, wenn er ein spezielles Problem hat, welches er mit den Klassen nicht lösen kann, was in der Praxis selten vorkommen dürfte.

Aus Ulis Frage konnte ich leider nicht herauslesen, ob er ein Newbie ist. Deswegen hatte ich die FileRead etc. Funktionen ins Feld geführt, um damit nicht ins OOP abzuschweifen sondern bei den Dateioperationen zu bleiben. An dieser Stelle könnte man jetzt wunderbar die Diskussion vollständige OOP gegen OOP mit prozeduraler Programmierung aufführen, aber das wollte ich eigentlich vermeiden :D
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Uli
Beiträge: 24
Registriert: Mi 2. Sep 2009, 18:49

Re: Sauberes Filehandling

Beitrag von Uli »

Danke noch mal für Eure Antworten.

Thema Newbie: Ich habe vor 25 Jahren mein erstes Turbo Pascal Programm geschrieben (natürlich ohne Exceptions).
Ich dache nur, dass so eine Frage in kein anders Forum passt.

Ich wollte auch keine Dikusion OOP vs. nicht OOP anzetteln.
Meine persönliche Meinung dazu ist OOP wo sinnvoll und ansonsten eben ohne, aber das muss jeder für sich entscheiden.

Nun noch mal zum Thema.
Bei allen meinen Programmen habe ich das Thema Fehler beim Schliessen einer Datei einfach ausgeblendet.

Code: Alles auswählen

{$I-} close(Txt); {$I+}
if ioresult <> o then;

Jetzt dachte ich, mit Exceptions kann man das ganz leicht sauber lösen, da habe ich mich wohl geirrt.
So sieht das dann wohl aus

Code: Alles auswählen

try
  try
    assign(Txt, 'test.txt');
    rewrite(Txt);
    writeln(Txt, 'test');
  finally
    Exc := ExceptObject;
    try
      close(Txt);
    except
      if Exc = Nil then raise;
    end;
  end;
except
  on E: Exception do writeln('Exception: ', E.Message);
end;

Ich glaube, da bleibe ich bei der Version ohne Fehler beim Schliessen abzufangen.

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

Re: Sauberes Filehandling

Beitrag von theo »

Uli hat geschrieben:Meine persönliche Meinung dazu ist OOP wo sinnvoll und ansonsten eben ohne, aber das muss jeder für sich entscheiden.


Meine persönliche Meinung zu OOP ist: immer! Und vor allem sollte man die Möglichkeiten nutzen, sie machen das Leben wirklich einfacher.
Ich benutze kaum noch Array of String oder AssignFile etc., ausser es gibt einen SEHR guten Grund dafür.
Es lohnt sich fast nie. Wenn ich sowas wie deinen Code-Snippet nur schon angucke, wird mir schwindlig.
Vergleiche das doch mal mit Scotty's Vorschlag: viewtopic.php?p=36975#p36975
Da kann man sich doch viel besser dem wesentlichen der Anwendung widmen.

Uli
Beiträge: 24
Registriert: Mi 2. Sep 2009, 18:49

Re: Sauberes Filehandling

Beitrag von Uli »

Hi Theo,
der Schnippsel ist ja nur ein kleiner Auszug als Beispiel des Wesentlichen.
Natürlich steht zwischen Öffnen und Schliessen noch ordentlich Programm Code.
Eine der Stellen liest z.B. Zeilen aus einer Datei, das können locker einige Tausend Zeilen sein.
Die lade ich natürlich nicht auf einmal ins Memory, Zeile für Zeile lesen, in die Datenbank schreiben, nach 100 Zeilen ein Commit und in einer Datei festhalten wieviele Zeilen ich schon in die Datenbank geschrieben habe um nach einem Programm oder Server Absturz dort wieder aufsetzen zu können. Da nützt mir ein Objekt auch nicht wirklich. Die Arbeit bleibt und die möglichen Fehler auch. Da es ein Service werden soll der im Hintergrund läuft, versuche ich das Programm klein, schnell und möglichst Fehlerfrei zu schreiben. Auch soll das Programm alle Exceptions überleben und entsprechend zu behandeln (Gegen Memory, CPU Fehler und Ähnliches ist natürlich kein Gras gewachsen).

Scotty
Beiträge: 768
Registriert: Mo 4. Mai 2009, 13:24
OS, Lazarus, FPC: Arch Linux, Lazarus 1.3 r44426M FPC 2.6.4
CPU-Target: x86_64-linux-qt/gtk2
Kontaktdaten:

Re: Sauberes Filehandling

Beitrag von Scotty »

Innerhalb einer finally-Anweisung finde ich das Abfangen von Exceptions wenig sinnvoll. Wenn die Verschachtelung unbedingt sein muss (man kann potentiellen Fehlern auch anders als per except begegnen), dann maximal so:

Code: Alles auswählen

try
  try
  except
  end;
finally
end;

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

Re: Sauberes Filehandling

Beitrag von theo »

@Uli: Vielleicht ist deine Anwendung tatsächlich die Ausnahme von der Regel.
Aber auch "einige Tausend Zeilen" sind ja nicht wirklich das Problem.
10'000 Zeilen à 80 Zeichen sind bspw. rund 782 kB. Welchen Rechner interessiert das heute schon?
Was kriege ich dafür: übersichtlichen, fehlerunanfälligen, gut getesteten Code.
Ich spreche von TStringList, mit OOP an sich hat das auch nichts zu tun.

Uli
Beiträge: 24
Registriert: Mi 2. Sep 2009, 18:49

Re: Sauberes Filehandling

Beitrag von Uli »

Hi Scotty, hier ein Auszug aus der Delphi Hilfe:
"If an exception is raised but not handled in the finally clause, that exception is propagated out of the try...finally statement, and any exception already raised in the try clause is lost. The finally clause should therefore handle all locally raised exceptions, so as not to disturb propagation of other exceptions."

Antworten