Datensätze aus einer Datei löschen

Für Fragen von Einsteigern und Programmieranfängern...
Joachim Raap
Beiträge: 143
Registriert: Mo 30. Mär 2020, 12:37

Datensätze aus einer Datei löschen

Beitrag von Joachim Raap »

Tach auch,
ich habe da eine Datei generiert, in der Records gespeichert werden. Die kann ich auch schön ergänzen und in einer ListView anzeigen.
Was ich nicht finde ist eine Möglichkeit, einzeln Datensätze wieder zu löschen. Die Vorgehensweise, alle Sätze bis auch denjenigen Welchen zu lesen, , und in eine neuer Datei zu speichern - die alte zu löschen und die neue umzubenenen ist wohl möglich aber scheint mir nicht der sinnvollste Weg zu sein.
Weiß jemand einen "vernünftigen" Weg (wohlgemerkt, im Hintergrund gibt es keine Datenbank!, nur eine schnöde Datei)

Vielen Dank

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

Re: Datensätze aus einer Datei löschen

Beitrag von theo »

Von wie vielen Millionen Datensätzen sprechen wir hier?
Die Daten in den Speicher lesen und die Datei neu schreiben (auch überschreiben) sollte auf einem PC anno domini 2022 normalerweise kein Problem sein.
Dafür würde ich keine Verrenkungen machen.
Wenn du besondere Bedingungen hast (z.B µController oder gigantische Datenmengen), dann müsstest du dies erwähnen.

Benutzeravatar
Winni
Beiträge: 1577
Registriert: Mo 2. Mär 2009, 16:45
OS, Lazarus, FPC: Laz2.2.2, fpc 3.2.2
CPU-Target: 64Bit
Wohnort: Fast Dänemark

Re: Datensätze aus einer Datei löschen

Beitrag von Winni »

Hi!

Bevor Datenbanken Open Source wurden (und also sauteuer waren) haben wir das so gemacht:

Wenn ein bestimmtes Integer-Feld kleiner Null war, oder ein bestimmtes String-Feld leer war, dann galt das Record als gelöscht und wurde nicht angezeigt.

Und als Nachtjob gab es das "krunchen" der gelöschten Records:
Die "gelöschten" Records wurden dann auch physikalisch gelöscht. Innerhalb der gleichen Datei, da Speicherplatz damals teuer und kostbar war.

Das Vorgehen haben wir uns bei dBase II abgeguckt, wo es explizit ein Boolsches Feld gab, das "deleted" bedeutete. Das schöne bei dieser Vorgehensweise: Wenn mal ein Record aus Versehen gelöscht war, so konnte man es wieder "undeleten".

winni

Joachim Raap
Beiträge: 143
Registriert: Mo 30. Mär 2020, 12:37

Re: Datensätze aus einer Datei löschen

Beitrag von Joachim Raap »

Danke Winni - ist immerhin eine Idee, die ich auch vorhatte umzusetzen - also nur logisch löschen und nicht physisch.
Ich weiß zwar noch nicht wirklich, wie ich das umsetze aber immerhin ist meine ursprüngliche Idee wieder aktiviert worden (man wie alt....)

Theo
Nicht böse sein; aber schau mal auf die Fragegruppe = Anfängefragen. Was für Dich einfach ist, ist derzeit noch ein Problem für mich und ich bin an Deinem Vorschlag bislang gescheitert.....

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

Re: Datensätze aus einer Datei löschen

Beitrag von theo »

Joachim Raap hat geschrieben:
Mo 24. Okt 2022, 20:02
Theo
Nicht böse sein; aber schau mal auf die Fragegruppe = Anfängefragen. Was für Dich einfach ist, ist derzeit noch ein Problem für mich und ich bin an Deinem Vorschlag bislang gescheitert.....
Ich bin nicht böse, du hast mich wohl falsch verstanden.
Ich meinte eigentlich nur "mach es nicht komplizierter als nötig".
Datensätze auf der Datei als gelöscht markieren ist für meine Begriffe kompliziert.

Wenn man dir helfen soll, dann müsstest du schon ungefähr sagen, wie ein Datensatz aussieht und wie viele es davon geben wird (Grössenordnung) und ggf., was du damit vor hast (Suchen, darstellen etc.).

Benutzeravatar
Winni
Beiträge: 1577
Registriert: Mo 2. Mär 2009, 16:45
OS, Lazarus, FPC: Laz2.2.2, fpc 3.2.2
CPU-Target: 64Bit
Wohnort: Fast Dänemark

Re: Datensätze aus einer Datei löschen

Beitrag von Winni »

@Joachim Raap

Was ich da obenstehed erläutert habe, waren UCSD-Pascal-Zeiten auf Apple ][ Clones (Basis 108 aus Münster), also Anfang der 80er.

Was für den Single-User-Betrieb und bei Datenmengen < 100.000 Records sehr gut geeignet ist, ist das StringGrid. Die Daten dann als CSV speichern. Geht alles sehr schnell. Und sortieren kann man (alphabetisch) nach beliebigen Spalten. Auch aasig schnell dank eingebautem Quicksort. Speichern und Laden geht nach vordefinierten Prozeduren und auch schnell.

Andererseits finde ich es ja gut, dass sich jemand mit eine File of Record beschäftigt und das alles"from the scratch" lernt.

Winni

Joachim Raap
Beiträge: 143
Registriert: Mo 30. Mär 2020, 12:37

Re: Datensätze aus einer Datei löschen

Beitrag von Joachim Raap »

Theo, der Record sieht so aus

TGrpSatz = record
GrpNr: integer;
GrpName: String[20];
GrpKto: integer;
GrpMonUms: real;
GrpJahrUms: real;
Aktiv:byte;
Anlage: string[8];
end;
Ich schätze, daß es davon im File nicht viel mehr als 100 oder 200 geben wird (nichts genaues weiß man nie :) ). Das Feld "Aktiv" habe ich schon mal aufgrund des logischen löschens eingebaut; angezeigt wird der Satz in einer ListView, wenn dieses Feld=0 ist. Beim Löschen erhält es 1 (so der Plan).
Für Deinen Hinweis habe ich - bislang erfolglos - diese Procedure gebaut::

procedure TFMGruppen.BTLoeschenClick(Sender: TObject);
var
i: integer;

begin
i:=0;
AssignFile(GrpDatei,'Gruppen.rec');
FileMode:=2;
ReSet(GrpDatei);
while (i<FileSize(GrpDatei)-1) and (not(EOF(GrpDatei))) do
begin
seek(GrpDatei,i);
read(GrpDatei,GrpSatz1);
if GrpSatz1.GrpNr<>5 then write(GrpDatei,GrpSatz1); //nur zum Test Nr.5
i:=i+1;
end;
closeFile(GrpDatei);
Anzeigen; //füllt die Listview mit den vorhandenen Sätzen
end;

Benutzeravatar
Winni
Beiträge: 1577
Registriert: Mo 2. Mär 2009, 16:45
OS, Lazarus, FPC: Laz2.2.2, fpc 3.2.2
CPU-Target: 64Bit
Wohnort: Fast Dänemark

Re: Datensätze aus einer Datei löschen

Beitrag von Winni »

Hi!


Mehrere Anmerkungen:

Fehler: Nach jedem read aus einem File steht der Filepoiner auf dem nächsten Record!
Bevor Du ein write in die Datei machst, musst Du ein weiteres

Code: Alles auswählen

seek(GrpDatei,i);
machen


Sinnlos:

In dem Bedingungen für die While-Schleife vermischt du Bytes (filesize) mit Records (i). Als Bedingung langt EOF vollständig. Das filesize ist sinnlos.

Kosmetik:

* Real ist finsterste Turbo-Pascal-Zeit. Letztes Jahrtausend.
Jetzt gibt es Single, Double und Extended.

* Weniger Tippen:

Für
i := i + 1;
gibt es
inc(i);

* Wir sind hier nicht bei C

Das Record-Feld Aktiv muss natürlich Boolean sein. Nur die C-Programmierer haben so etwas nicht und müssen bytes bemühen.

Zur Datenmenge:

Bei den heutigen Rechnern sind 100 oder 200 Records ein Witz. Wir haben Anfang der 80er auf einem 286er mit 1000 Records professionel mit einem sequentiellen File gearbeitet (Rechnungswesen). War nur als quick & dirty Provisiorium gedacht, verhielt sich aber gut mit seinen Antwortzeiten, dass es als Provisorium 3 oder 4 Jahre überlebt hat.Gab gerade Wichtigeres...

Winni

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

Re: Datensätze aus einer Datei löschen

Beitrag von theo »

@Joachim Raap: Es gibt viele Lösungen.
Ich habe dir mal ein Beispiel eines moderneren Ansatzes gemacht.
Du speicherst und liest so immer alles, das ist bei deinen Datenmengen kein Problem.
Du hast so einen komfortablen Zugriff und alle Methoden der Liste, wie MyList.Delete(1), Sort und so den ganzen Zauber gratis zur Verfügung! :wink:
Siehe auch:
https://www.freepascal.org/docs-html/rt ... glist.html
https://www.freepascal.org/docs-html/rt ... slist.html

Ist nur kurz hingehackt, müsste aber erstmal funktionieren.
Ob das in deinem Fall das Optimale ist, kann ich von hier aus nicht beurteilen.
Aber "Einsteigerfragen" heisst ja nicht, dass man alles zu Fuss und wie vor 50 Jahren machen muss. :wink:

Code: Alles auswählen

unit Unit1;

{$mode objfpc}{$H+}
{$MODESWITCH AdvancedRecords+}

interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ComCtrls, StdCtrls, fgl;

type

  { TGrpSatz }

  TGrpSatz = record
    GrpNr: integer;
    GrpName: string[20];
    GrpKto: integer;
    GrpMonUms: real;
    GrpJahrUms: real;
    Aktiv: byte;
    Anlage: string[8];
    class operator = (Left, Right: TGrpSatz): boolean;  //AdvancedRecords
  end;

  TGrpSatzListGen = specialize TFpgList<TGrpSatz>;

  { TGrpSatzList }

  TGrpSatzList = class(TGrpSatzListGen)
  public
    procedure SaveToFile(FileName: string);
    procedure LoadFromFile(FileName: string);
  end;

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    ListView1: TListView;
    procedure Button1Click(Sender: TObject);
  private

  public

  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

{ TGrpSatzList }

procedure TGrpSatzList.SaveToFile(FileName: string);
var
  fs: TFileStream;
  AGrpSatz: TGrpSatz;
begin
  fs := TFileStream.Create(Filename, fmCreate);
  try
    fs.Write(Count, SizeOf(cardinal));
    for AGrpSatz in Self do
      fs.Write(AGrpSatz, SizeOf(TGrpSatz));
  finally
    fs.Free;
  end;
end;

procedure TGrpSatzList.LoadFromFile(FileName: string);
var
  fs: TFileStream;
  i: integer;
  NumRecords: cardinal;
  AGrpSatz: TGrpSatz;
begin
  Clear;
  fs := TFileStream.Create(Filename, fmOpenRead);
  try
    fs.Read(NumRecords, SizeOf(cardinal));
    for i := 0 to NumRecords - 1 do
    begin
      fs.Read(AGrpSatz, SizeOf(TGrpSatz));
      Add(AGrpSatz);
    end;
  finally
    fs.Free;
  end;
end;

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
var
  MyList: TGrpSatzList;
  AGrpSatz: TGrpSatz;
  ListItem: TListItem;
begin
  MyList := TGrpSatzList.Create;
  //Mit Demodaten füllen
  AGrpSatz.GrpNr := 1;
  AGrpSatz.GrpName := 'test0';
  MyList.Add(AGrpSatz);
  
  AGrpSatz.GrpNr := 2;
  AGrpSatz.GrpName := 'test1';
  MyList.Add(AGrpSatz);
  
  AGrpSatz.GrpNr := 3;
  AGrpSatz.GrpName := 'test2';
  MyList.Add(AGrpSatz);
  
  //Alles Speichern
  MyList.SaveToFile('testfile.dat');
  //Freigeben
  MyList.Free;
  //****************************************

  MyList := TGrpSatzList.Create;
  //Von Datei laden
  MyList.LoadFromFile('testfile.dat');
  //Den zweiten Eintrag löschen.
  MyList.Delete(1);
  //ListView befüllen
  for AGrpSatz in MyList do
  begin
   ListItem:=ListView1.Items.Add;
   ListItem.Caption:=AGrpSatz.GrpNr.ToString;
   ListItem.SubItems.Add(AGrpSatz.GrpName);
  end;
  MyList.Free;
end;


{ TGrpSatz }

//Artefakt, notwendig aber vergiss das

class operator TGrpSatz. = (Left, Right: TGrpSatz): boolean;
begin
  Result := Left.GrpNr < Right.GrpNr;
end;

end.

Joachim Raap
Beiträge: 143
Registriert: Mo 30. Mär 2020, 12:37

Re: Datensätze aus einer Datei löschen

Beitrag von Joachim Raap »

Winni
Ja, mit Turbo-Pascal habe ich angefangen und daher kommen die Basics. Als Borland dann mit objektorientiertem Pascal kam (war glaube ich Version 5.5) habe ich aufgegeben, weil ich es einfach nicht verstanden habe. Das jetztige Lazarus ist ein neuer Versuch mit objektorientierter Programmierung (nur so für mich mit fiktiven Aufgaben)....

Theo
"ist nur kurz hingehackt, müsste aber erstmal funktionieren." Da fällt mich nur "ach Du lieber Gott ein". Hier wird der Unterschied zwischen Profi und Anfänger richtig deutlich - ich werde wohl eine etwas längere Weile brauchen um das zu verstehen, was Du "kurz hingehackt" hast (vielleicht auch nie). Auf jeden Fall danke für Deine Mühe (ich schäme mich ein wenig.... :oops: )

Danke all

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6209
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Datensätze aus einer Datei löschen

Beitrag von af0815 »

Joachim Raap hat geschrieben:
Di 25. Okt 2022, 04:12
(vielleicht auch nie). Auf jeden Fall danke für Deine Mühe (ich schäme mich ein wenig.... :oops: )
Das brauchst du nicht, warum auch. Wichtig ist, wenn man Wissen haben will, da zu erklären was man braucht und es anzunehmen. Das Danke ist (heutzutage seltener) Lohn für die Arbeit.

Ich glaube, das ich mittlerweile mehr vergesse als ich lerne :shock: :D :mrgreen:

Es schadet nicht auch mal in der Lazarus/FPC Wiki vorbeischauen. Dort ist sehr viel Wissen (manchmal chaotisch von den Versionen und Sprachen her) versteckt.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

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

Re: Datensätze aus einer Datei löschen

Beitrag von theo »

Joachim Raap hat geschrieben:
Di 25. Okt 2022, 04:12
Theo
"ist nur kurz hingehackt, müsste aber erstmal funktionieren." Da fällt mich nur "ach Du lieber Gott ein". Hier wird der Unterschied zwischen Profi und Anfänger richtig deutlich - ich werde wohl eine etwas längere Weile brauchen um das zu verstehen, was Du "kurz hingehackt" hast (vielleicht auch nie). Auf jeden Fall danke für Deine Mühe (ich schäme mich ein wenig.... :oops: )
"Kurz hingehackt" heisst nicht, dass es für mich ohne Anstrengung war, es heisst nur, dass die Aufgabe verschiedene Wege zulässt und das ist einer davon, den ich vor dem zu Bett gehen noch so weit geschafft habe, dass er läuft.
"Kurz hingehackt" heisst, dass ich nicht bei jedem Schritt 3 Stunden überlegt habe, wie man es auch anders machen könnte und es heisst v.A. nicht, dass dies ein grandioses Demo Programm mit pädagogischem Anspruch ist.
Aber es ist soweit korrekt und läuft.

Noch kurz, wie du das testen kannst:
  • Mach ein neues Projekt.
  • Zieh einen TButton und ein TListview auf das Formular.
  • Doppelklicke auf den Button damit das OnClick Code-Gerüst erstellt wird.
  • Füge der TListview zwei Columns hinzu.
Ändere keine Bezeichner.
  • Dann überschreibe den Quellcode von Unit1 komplett mit dem Code von oben.
Dann müsste es laufen.

Die kompakteste Einführung in OOP ist mMn diese:
https://www.delphi-treff.de/tutorials/o ... ashkurs/8/
Darf man auch zweimal lesen.

Du musst nicht immer gleich alles 100%ig verstehen. Versuch durch Analogien das Beispiel zu erweitern.
Teile den Code im Button1Click Ereignis in mehrere Teile/Ereignisse auf.
Bringe die anderen Variablen von TGrpSatz ins Spiel etc.
Den Code in LoadFrom-/SaveToFile kannst du so lassen, den brauchst du nicht mehr verändern.
Oft reicht es erst einmal, wenn man einen Code so hinbiegen kann, dass er tut, was man will, auch ohne jedes Detail zu verstehen. Das gibt einem schon einmal das gute Gefühl, Herr der Lage zu sein. :wink:

Joachim Raap
Beiträge: 143
Registriert: Mo 30. Mär 2020, 12:37

Re: Datensätze aus einer Datei löschen

Beitrag von Joachim Raap »

Theo, habe ich wie angeleitet gemacht. In der View werden die Werte 1 und 3 untereinander ausgegeben, wenn ich den Button clicke. Daneben gibt es bei der Compilierung jede Menge Hinweise:

Projekt kompilieren, Ziel: C:\Users\Josch\AppData\Local\Temp\project1.exe: Erfolg, Hinweise: 26
fgl.pp(930,1) Hint: "inherited" not yet supported inside inline procedure/function
fgl.pp(930,1) Hint: Inlining disabled
fgl.pp(945,1) Hint: "inherited" not yet supported inside inline procedure/function
fgl.pp(945,1) Hint: Inlining disabled
fgl.pp(1005,1) Hint: "inherited" not yet supported inside inline procedure/function
fgl.pp(1005,1) Hint: Inlining disabled
fgl.pp(1010,1) Hint: "inherited" not yet supported inside inline procedure/function
fgl.pp(1010,1) Hint: Inlining disabled
fgl.pp(963,1) Hint: "inherited" not yet supported inside inline procedure/function
fgl.pp(963,1) Hint: Inlining disabled
fgl.pp(968,1) Hint: "inherited" not yet supported inside inline procedure/function
fgl.pp(968,1) Hint: Inlining disabled
fgl.pp(950,1) Hint: "inherited" not yet supported inside inline procedure/function
fgl.pp(950,1) Hint: Inlining disabled
fgl.pp(955,1) Hint: "inherited" not yet supported inside inline procedure/function
fgl.pp(955,1) Hint: Inlining disabled
fgl.pp(997,1) Hint: "inherited" not yet supported inside inline procedure/function
fgl.pp(997,1) Hint: Inlining disabled
fgl.pp(1023,7) Note: Call to subroutine "function TFpgList<Unit1.TGrpSatz>.Add(const Item:TGrpSatz):LongInt;" marked as inline is not inlined
fgl.pp(1023,20) Note: Call to subroutine "function TFpgList<Unit1.TGrpSatz>.Get(Index:LongInt):<record type>;" marked as inline is not inlined
unit1.pas(82,23) Hint: Local variable "NumRecords" does not seem to be initialized
unit1.pas(85,23) Hint: Local variable "AGrpSatz" does not seem to be initialized
unit1.pas(86,7) Note: Call to subroutine "function TFpgList<Unit1.TGrpSatz>.Add(const Item:TGrpSatz):LongInt;" marked as inline is not inlined
unit1.pas(105,3) Note: Call to subroutine "function TFpgList<Unit1.TGrpSatz>.Add(const Item:TGrpSatz):LongInt;" marked as inline is not inlined
unit1.pas(109,3) Note: Call to subroutine "function TFpgList<Unit1.TGrpSatz>.Add(const Item:TGrpSatz):LongInt;" marked as inline is not inlined
unit1.pas(113,3) Note: Call to subroutine "function TFpgList<Unit1.TGrpSatz>.Add(const Item:TGrpSatz):LongInt;" marked as inline is not inlined

Meine Bemerkung, daß ich wohl längere Zeit brauche um das zu verstehen was Du gemacht hast bedeutete, daß ich jetzt z.B.mit den Meldungen nichts anfangen kann und mögliche Fehler auch nicht beheben kann. Auch habe ich nicht verstanden, ,die Werte 1 und 3 kommen; Aber alles egal, ich werde mich schon irgendwie durchkämpfen.
Das nur als Rückmeldung und nochmals danke.

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

Re: Datensätze aus einer Datei löschen

Beitrag von theo »

@Joachim Raap: Hints und Notes kannst du vergessen. Diese hier sind mehrheitlich Fehlalarme, bzw. für den User nicht zu lösen.
Warnings und Errors sollte man beachten.

Der Rest ist schon richtig.
Schaue dir einfach den Code in Button1Click in Ruhe an.
Die Werte werden ja dort eingefüllt (Siehe //Mit Demodaten füllen)
Zwischenzeitlich werden die Datensätze in die Datei "testfile.dat" geschrieben und von dort wieder ausgelesen.
Es werden nur 1 und 3 angezeigt wegen der Zeile:

Code: Alles auswählen

  //Den zweiten Eintrag löschen.
  MyList.Delete(1);
Was verstehst du denn nicht?

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1435
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Datensätze aus einer Datei löschen

Beitrag von fliegermichl »

theo hat geschrieben:
Di 25. Okt 2022, 08:33
...
Die kompakteste Einführung in OOP ist mMn diese:
https://www.delphi-treff.de/tutorials/o ... ashkurs/8/
Darf man auch zweimal lesen.
...
Ich hab mir das mal durchgelesen. Es ist alles korrekt was er da so schreibt, überfordert einen Neuling aber mit absoluter Sicherheit.
Besser wäre mMn. einen Neuling Schritt für Schritt in das Thema zu führen.

Was soll ein Anfänger mit "Sichtbarkeit von Feldern und Methoden" anfangen?
oder das auf private Felder anders als in anderen Programmiersprachen aus Klassen der gleichen Unit zugegriffen werden kann. (Was ich übrigens für einen Fehler halte, da war wieder die Delphi Kompatibilitätskuh am Werk)

Antworten