AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

ForceFile

Ein Thema von Monday · begonnen am 23. Jul 2022 · letzter Beitrag vom 25. Jul 2022
Antwort Antwort
Delphi.Narium

Registriert seit: 27. Nov 2017
2.554 Beiträge
 
Delphi 7 Professional
 
#1

AW: ForceFile

  Alt 24. Jul 2022, 10:49
Das würd' ich dann aber noch etwas anpassen:
Delphi-Quellcode:
function ForceFile(dateiname: string; bereits_vorhandene_datei_trotzdem_neu_erstellen: Boolean = False) : Boolean;
var
  f: Text;
begin
  Result := ForceDirectories(ExtractFilePath(Dateiname));
  if Result then begin
    if not FileExists(dateiname) or bereits_vorhandene_datei_trotzdem_neu_erstellen then begin
      AssignFile(f, dateiname);
      ReWrite(f);
      CloseFile(f);
    end;
  end;
  Result := FileExists(dateiname);
end;
...
if ForceFile(datei) then begin
  Stringlist.Create;
  StringList.LoadfromFile(datei);
  ...
  StringList.SaveToFile(datei);
  StringList.Free;
end else begin
  // Fehlerbehandlung ...
end;
Bei fehlender Verzeichnisstruktur wird Deine Variante (vermutlich) scheitern, himitsus Einzeiler if FileExists(...) then SL.LoadFromFile(..); ebenfalls, wenn die Stringliste per SaveToFile gespeichert werden soll.
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.316 Beiträge
 
Delphi 12 Athens
 
#2

AW: ForceFile

  Alt 24. Jul 2022, 11:14
Das ForceDirectory reicht beim Speichern.

Beim Laden ist es egal.
Die Datei ist so oder so nicht da (oder leer), egal ob man das Verzeichnis und/oder die Datei noch erstellt.
Und eine dann neue leere Datei zu laden macht auch keinen Sinn. (*1)


*1) Einzig um beim Laden bereits zu prüfen, ob die Datei später auch gespeichert werden kann,
aber auch a kann man direkt mir einem Read/Write-Schreibzugriff die Datei öffnen (ohne ihren Inhalt zu löschen) und direkt laden.
Ein Therapeut entspricht 1024 Gigapeut.

Geändert von himitsu (24. Jul 2022 um 11:17 Uhr)
  Mit Zitat antworten Zitat
Delphi.Narium

Registriert seit: 27. Nov 2017
2.554 Beiträge
 
Delphi 7 Professional
 
#3

AW: ForceFile

  Alt 24. Jul 2022, 11:52
*1) Einzig um beim Laden bereits zu prüfen, ob die Datei später auch gespeichert werden kann,
aber auch a kann man direkt mir einem Read/Write-Schreibzugriff die Datei öffnen (ohne ihren Inhalt zu löschen) und direkt laden.
Mir ging's darum, am Anfang zu prüfen, ob die Datei nicht nur geladen werden kann (sofern vorhanden), sondern auch am Anfang schon (möglichst) sicherzustellen, dass sie im späteren Programmverlauf auch geschrieben werden kann.

Bei 'ner Logdatei ist's halt "blöde", wenn nach stundenlangem Programmlauf festgestellt wird, dass die Logdatei nicht geschrieben werden kann. Ist halt ärgerlich, kommt aber zuweilen schonmal im realen Leben vor.

Daher mach' ich lieber ein paar Prüfungen zuviel, als irgendwann auf die Nase zu fallen.

Wenn das ForceDirectories bereits am Anfang scheitert, weiß man, dass man nicht sinnvoll weitermachen kann, auch wenn das ForceDirectories letztlich unmittelbar vor dem SaveToFile ausreichen würde.

Natürlich ist es nicht sinnvoll eine leere Datei zuladen, Nichts in 'ner Stringliste ist halt eben Nichts in 'ner Stringliste, aber ich weiß dann schonmal sicher, dass ich Nichts habe und muss nicht mehr damit rechnen, dass ich im weiteren Programmverlauf nichtmal Nichts haben werde

Beim ForceFile weiß ich aber, dass das Verzeichis erstellt werden kann und das in dem Verzeichnis eine Datei erstellt werden kann. Man könnte die Funktion noch um eine IO-Prüfung erweiteren, so dass damit dann auch noch eventuell vorhandene Rechteprobleme, ... festgestellt werden könnten und dort einem nicht unerwarten eine Exception um die Ohren fliegt.
Delphi-Quellcode:
function ForceFile(dateiname: string; bereits_vorhandene_datei_trotzdem_neu_erstellen: Boolean = False) : Integer;
var
  f : Text;
  bOk : Boolean;
  iIOResult : Integer;
begin
  Result := 0;
  bOk := ForceDirectories(ExtractFilePath(Dateiname));
  if bOk then begin
    if not FileExists(dateiname) or bereits_vorhandene_datei_trotzdem_neu_erstellen then begin
      AssignFile(f, dateiname);
      {$I-}
      ReWrite(f);
      {$I+}
      Result := IOResult;
      CloseFile(f);
    end;
  end else begin
    Result := -MaxInt;
  end;
end;
...
iIOResult := ForceFile(datei);
case iIOResult of
  0 : begin
        Stringlist.Create;
        StringList.LoadfromFile(datei);
        ...
        StringList.SaveToFile(datei);
        StringList.Free;
     end;
  // hier ggfls. andere Werte von iIOResult gezielt abfragen und entsprechend reagieren.
  else
    // Fehlerbehandlung ...
  end;
end;
Und ja, die Frage ist, wieviel Aufwand will man da treiben?
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.316 Beiträge
 
Delphi 12 Athens
 
#4

AW: ForceFile

  Alt 24. Jul 2022, 12:23
Man kann sich ja einfach mal den Code des LoadFromFile vornehmen und dort den Dateizugriff anpassen.
In diesem Fall also OPEN_EXISTING durch OPEN_ALWAYS ersetzen und zusätzlich auch den Schreibzugriff zu aktivieren (siehe SaveToFile).
https://docs.microsoft.com/en-us/win...#CREATE_ALWAYS

Delphi-Quellcode:
uses System.RTLConsts;

procedure TForm9.FormCreate(Sender: TObject);
var
  FN: string;
  SL: TStringList;
  FS: {TFileStream}THandleStream;
  FH: THandle;
begin
  FN := ChangeFileExt(Application.ExeName, '.txt');
  SL := TStringList.Create;
  try
    ForceDirectories(ExtractFileDir(FN));
    FH := CreateFile(PChar(FN), GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ, nil,
      {OPEN_EXISTING}OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_SEQUENTIAL_SCAN, 0);
    if FH = INVALID_HANDLE_VALUE then
      //RaiseLastOSError;
      raise EFCreateError.CreateFmt(SFCreateErrorEx, [ExpandFileName(FN), SysErrorMessage(GetLastError)]);
    try
      FS := THandleStream.Create(FH); // hatte der HandleStream nicht mal ein "Owns" und konnte das Handle dann selber freigeben?
      try
        SL.LoadFromStream(FS);
      finally
        FS.Free;
      end;
    finally
      CloseHandle(FH);
    end;

    //...

    SL.SaveToFile(FN);
  finally
    SL.Free;
  end;
end;
Alternativ alles zufammenfassen und daraus nur eine "Prüfung" bauen.

Delphi-Quellcode:
procedure TForm9.FormCreate(Sender: TObject);
var
  FN: string;
  SL: TStringList;
  FH: THandle;
begin
  FN := ChangeFileExt(Application.ExeName, '.txt');
  SL := TStringList.Create;
  try
    ForceDirectories(ExtractFileDir(FN));
    FH := CreateFile(PChar(FN), GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ, nil,
      {OPEN_EXISTING}OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_SEQUENTIAL_SCAN, 0);
    if FH = INVALID_HANDLE_VALUE then
      //RaiseLastOSError;
      raise EFCreateError.CreateFmt(SFCreateErrorEx, [ExpandFileName(FN), SysErrorMessage(GetLastError)]);
    CloseHandle(FH);

    SL.LoadFromFile(FN);

    //...

    SL.SaveToFile(FN);
  finally
    SL.Free;
  end;
end;


Der FileHandle (FH) und FileStream (FS) ließen sich auch bis zum Ende behalten und direkt zum Schreiben benutzen.
Da kann zwischendrin auch nicht die Datei, bzw. das Schreibrecht und das Sharingrecht verschwieden, weil es die ganze Zeit bestehen bleibt.

Delphi-Quellcode:
procedure TForm9.FormCreate(Sender: TObject);
var
  FN: string;
  SL: TStringList;
  FS: {TFileStream}THandleStream;
  FH: THandle;
begin
  FN := ChangeFileExt(Application.ExeName, '.txt');
  SL := TStringList.Create;
  try
    ForceDirectories(ExtractFileDir(FN));
    FH := CreateFile(PChar(FN), GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ, nil,
      {OPEN_EXISTING}OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_SEQUENTIAL_SCAN, 0);
    if FH = INVALID_HANDLE_VALUE then
      //RaiseLastOSError;
      raise EFCreateError.CreateFmt(SFCreateErrorEx, [ExpandFileName(FN), SysErrorMessage(GetLastError)]);
    try
      FS := THandleStream.Create(FH);
      try
        SL.LoadFromStream(FS);

        //...

        FS.Position := 0;
        FS.Size := 0; // oder nach dem Speichern ein Truncate
        SL.SaveToStream(FS);
        //FS.Truncate; // gibt es nicht, aber Size zuweisen geht ja auch, sowie auch über das Handle
        // also FS.Size := FS.Position;
        // oder SetEndOfFile(FH);
      finally
        FS.Free;
      end;
    finally
      CloseHandle(FH);
    end;
  finally
    SL.Free;
  end;
end;
Ein Therapeut entspricht 1024 Gigapeut.

Geändert von himitsu (24. Jul 2022 um 12:42 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.603 Beiträge
 
Delphi 12 Athens
 
#5

AW: ForceFile

  Alt 24. Jul 2022, 13:46
Man kann sich ja einfach mal den Code des LoadFromFile vornehmen und dort den Dateizugriff anpassen.
Etwa so?
Delphi-Quellcode:
type
  TStringsHelper = class helper for TStrings
    procedure LoadFromFile(const FileName: string); overload;
    procedure LoadFromFile(const FileName: string; Encoding: TEncoding); overload;
  end;

procedure TStringsHelper.LoadFromFile(const FileName: string; Encoding: TEncoding);
begin
  if FileExists(FileName) then
    inherited;
end;

procedure TStringsHelper.LoadFromFile(const FileName: string);
begin
  if FileExists(FileName) then
    inherited;
end;
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.316 Beiträge
 
Delphi 12 Athens
 
#6

AW: ForceFile

  Alt 24. Jul 2022, 14:36
Ja, für das reine Abfangen der Exception, wenn die Datei noch nicht existiert.

Mach noch ein SaveToFile ans Ende deiner Methoden, dann hast auch gleich sofort den Schreibzugriff mit geprüft.

Und/oder ins SaveToFile zumindestens noch ein ForceDirectory.
Ein Therapeut entspricht 1024 Gigapeut.

Geändert von himitsu (24. Jul 2022 um 14:38 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von KodeZwerg
KodeZwerg

Registriert seit: 1. Feb 2018
3.691 Beiträge
 
Delphi 11 Alexandria
 
#7

AW: ForceFile

  Alt 24. Jul 2022, 14:46
Man kann sich ja einfach mal den Code des LoadFromFile vornehmen und dort den Dateizugriff anpassen.
In diesem Fall also OPEN_EXISTING durch OPEN_ALWAYS ersetzen und zusätzlich auch den Schreibzugriff zu aktivieren (siehe SaveToFile).
https://docs.microsoft.com/en-us/win...#CREATE_ALWAYS

Delphi-Quellcode:
uses System.RTLConsts;

procedure TForm9.FormCreate(Sender: TObject);
var
  FN: string;
  SL: TStringList;
  FS: {TFileStream}THandleStream;
  FH: THandle;
begin
  FN := ChangeFileExt(Application.ExeName, '.txt');
  SL := TStringList.Create;
  try
    ForceDirectories(ExtractFileDir(FN));
    FH := CreateFile(PChar(FN), GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ, nil,
      {OPEN_EXISTING}OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_SEQUENTIAL_SCAN, 0);
    if FH = INVALID_HANDLE_VALUE then
      //RaiseLastOSError;
      raise EFCreateError.CreateFmt(SFCreateErrorEx, [ExpandFileName(FN), SysErrorMessage(GetLastError)]);
    try
      FS := THandleStream.Create(FH); // hatte der HandleStream nicht mal ein "Owns" und konnte das Handle dann selber freigeben?
      try
        SL.LoadFromStream(FS);
      finally
        FS.Free;
      end;
    finally
      CloseHandle(FH);
    end;

    //...

    SL.SaveToFile(FN);
  finally
    SL.Free;
  end;
end;
Alternativ alles zufammenfassen und daraus nur eine "Prüfung" bauen.

Delphi-Quellcode:
procedure TForm9.FormCreate(Sender: TObject);
var
  FN: string;
  SL: TStringList;
  FH: THandle;
begin
  FN := ChangeFileExt(Application.ExeName, '.txt');
  SL := TStringList.Create;
  try
    ForceDirectories(ExtractFileDir(FN));
    FH := CreateFile(PChar(FN), GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ, nil,
      {OPEN_EXISTING}OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_SEQUENTIAL_SCAN, 0);
    if FH = INVALID_HANDLE_VALUE then
      //RaiseLastOSError;
      raise EFCreateError.CreateFmt(SFCreateErrorEx, [ExpandFileName(FN), SysErrorMessage(GetLastError)]);
    CloseHandle(FH);

    SL.LoadFromFile(FN);

    //...

    SL.SaveToFile(FN);
  finally
    SL.Free;
  end;
end;


Der FileHandle (FH) und FileStream (FS) ließen sich auch bis zum Ende behalten und direkt zum Schreiben benutzen.
Da kann zwischendrin auch nicht die Datei, bzw. das Schreibrecht und das Sharingrecht verschwieden, weil es die ganze Zeit bestehen bleibt.

Delphi-Quellcode:
procedure TForm9.FormCreate(Sender: TObject);
var
  FN: string;
  SL: TStringList;
  FS: {TFileStream}THandleStream;
  FH: THandle;
begin
  FN := ChangeFileExt(Application.ExeName, '.txt');
  SL := TStringList.Create;
  try
    ForceDirectories(ExtractFileDir(FN));
    FH := CreateFile(PChar(FN), GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ, nil,
      {OPEN_EXISTING}OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_SEQUENTIAL_SCAN, 0);
    if FH = INVALID_HANDLE_VALUE then
      //RaiseLastOSError;
      raise EFCreateError.CreateFmt(SFCreateErrorEx, [ExpandFileName(FN), SysErrorMessage(GetLastError)]);
    try
      FS := THandleStream.Create(FH);
      try
        SL.LoadFromStream(FS);

        //...

        FS.Position := 0;
        FS.Size := 0; // oder nach dem Speichern ein Truncate
        SL.SaveToStream(FS);
        //FS.Truncate; // gibt es nicht, aber Size zuweisen geht ja auch, sowie auch über das Handle
        // also FS.Size := FS.Position;
        // oder SetEndOfFile(FH);
      finally
        FS.Free;
      end;
    finally
      CloseHandle(FH);
    end;
  finally
    SL.Free;
  end;
end;
Du hast seinen Wunsch einer "leeren" Datei vergessen glaube ich. Also eine existierende Datei "leeren".
Gruß vom KodeZwerg
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.316 Beiträge
 
Delphi 12 Athens
 
#8

AW: ForceFile

  Alt 24. Jul 2022, 14:50
Nein.

Die Hilfe lesen?
MSDN-Library durchsuchenOPEN_ALWAYS



@Uwe: Schöner wäre es, wenn Die Load/Save ohne Encoding auf die Version mit Encoding gehen würden, anstatt den Stream jeder selber zu erstellen.
So hätte man statt Helper die Klasse unterm selben Namen ableiten und nur zwei Funktion (die mit Encoding) überschreiben müssen.

Am Ende landet ja eh alles bei LoadFromStream/SaveToStream mit Encoding.
Ein Therapeut entspricht 1024 Gigapeut.
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.603 Beiträge
 
Delphi 12 Athens
 
#9

AW: ForceFile

  Alt 24. Jul 2022, 16:37
@Uwe: Schöner wäre es, wenn Die Load/Save ohne Encoding auf die Version mit Encoding gehen würden, anstatt den Stream jeder selber zu erstellen.
So hätte man statt Helper die Klasse unterm selben Namen ableiten und nur zwei Funktion (die mit Encoding) überschreiben müssen.
https://quality.embarcadero.com/browse/RSP-38725
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:14 Uhr.
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz