Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Element aus Dynamischem Array entfernen?


Delete - Sa 10.07.04 12:17
Titel: Element aus Dynamischem Array entfernen?
Hallo,
Ich bin dabei ein kleines Verwaltungsprogramm zu schreiben. Um einträge zu löschen, müsste ich wissen, wie ich aus einem dynamischen Array ein Element "heraustrenne". Praktisch so, dass die übrigen Elemente eins aufrutschen.


Delphi-Quelltext
1:
2:
var Liste: array of TDatenRec;
SetLength(Liste, 20);


Nun soll z.B. das 10. Element enternt werden und die Liste soll danach noch 19 Elemente haben.

Dankeim Voraus!


Moderiert von user profile iconPeter Lustig: Topic aus VCL (Visual Component Library) verschoben am Sa 10.07.2004 um 12:20


Schattengeist - Sa 10.07.04 12:20

'per Hand'
Alle hochrücken, dann die Liste kürzen.
Hmmm. Wir verwenden für sowas StingListen/ObjektListen. Da ist sowas alles schon drin. Man muß nur das alles als Objekte vorliegen haben.


Muetze1 - Sa 10.07.04 12:39

Moin!

Wieso Objekte? Nimm TList und dann kannste das gleiche auch mit deinen Records machen...

MfG
Muetze1


bms - Sa 10.07.04 12:56
Titel: Re: Element aus Dynamischem Array entfernen?
Elite* hat folgendes geschrieben:
Um einträge zu löschen, müsste ich wissen, wie ich aus einem dynamischen Array ein Element "heraustrenne". Praktisch so, dass die übrigen Elemente eins aufrutschen.


Delphi-Quelltext
1:
2:
var Liste: array of TDatenRec;
SetLength(Liste, 20);


Nun soll z.B. das 10. Element enternt werden und die Liste soll danach noch 19 Elemente haben.


Wie gesagt per Hand. Allerdings kannst du das ein wenig anders angehen. Du löscht den Wert nicht, sondern gibst ihm eine Löschmarke. In deinem Programm achtest du dann, daß du nur Werte verwendest die keine Löschmarke haben. Zur gegebner Zeit kann der User dann die Datenbank komprimieren. Hier wird dann nicht gezipt, sondern der Array wird von gelöschten Werten befreit. Anders machen es übrigens auch die Datenbanken nicht. Sie belassen die gelöschten Werte und löschen die erst wenn die Datenbank komprimiert wird.


Schattengeist - Sa 10.07.04 13:02

Da es sich nur um Speichertabellen handelt, finde ich, das Löschmarken etwas zu aufwendig sind.
Mal eben kurz das zu eleminieren sollte nicht soooo zeitfressend sein.


Gausi - Sa 10.07.04 13:45

naja...für solche Operationen würde ich trotzdem dringendst ein Listenstruktur empfehlen. Besonders dann, wenn solche Lösch- (und Einfüge-) operationen öfter auftreten, und die Anzahl der Objekte groß ist.


Delete - Sa 10.07.04 17:39

Wenn ich jetzt erfolgreich ein Element draußen habe und das Array somit eines kürzer ist (19 statt vorher 20), muss ich ja das 20. Element praktisch löschen bzw. freigeben.

Geht einfach
SetLength(Liste, 19);
oder muss da was anderes her?


Schattengeist - Sa 10.07.04 18:18

Was ist TDatenRec? Ein Objekt? dann etwas lustiger. Ein Record? Dann einfach die Liste kürzen.


iX0r - Sa 10.07.04 19:51

hier empfiehlt sich als datenstruktur natürlich eine verkette liste.
damit kannst du elemente sehr einfach entfernen, bedingung ist natürlich, daß du dich wein wenig mit pointern auskennst.
mfg
iX0r


Delete - Sa 10.07.04 21:11

Mit Hilfe der Suchfunktion und des eigenen gehirns gelöst. ^^


Tino - So 11.07.04 09:24

Poste doch mal deine Lösung... würde mich interessieren :-)


Delete - So 11.07.04 20:40


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
procedure TForm1.DeleteArrayIndex(var X: TListe; Index: Integer);
var a:integer;
begin
  if Index > High(X) then exit;
  if Index < Low(X) then exit;
  if Index = High(X) then
  begin
    SetLength(X, Length(X) - 1);
    Exit;
  end;
  for a:= index to length(X)-2 do
    X[a]:=X[a+1];
  SetLength(X, Length(X) - 1);
end;

...

DeleteArrayIndex(Liste, ListBox1.ItemIndex);


So viel schonmal dazu.
Nun haben sich bei mir allerdings weitere Probleme ergeben. Ich möhte das dynamische Array mit Hilfe eines FileStreams in eine Datei bringen und zwar in folgender Struktur:

Passwort: String;
Length(Liste): Integer;
Und nun die Array-Elemente...


Bei Öffnen werden willkürlich irgendwelche Speicherbereiche ausgelesen wie zB. der Inhalt der Variable "FileName" oder auch Captions einiger Labels. Es folgen die Deklarationen des TDatenRec, des dyn. Array TListe und der Speicher bzw. Öffnen-Code:



Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
type TDatenRec =  Record
      Titel,
      LoginName, Passwort,
      Information: String;
      end;

type Tliste = array of TDatenRec;

...

procedure TForm1.Speichern;
var
  stream: TFileStream;
  FileName: string;
  Laenge:Integer;
  a:integer;
begin
  FileName:=ExtractFilePath(Application.ExeName) + 'data.dat';
  if not fileexists(FileName) then
    begin
    stream:= TFilestream.Create(Filename, fmcreate);
    stream.write(Passwort, SizeOf(Passwort));
      a:=0//stream.write erfordert variable
    stream.write(a, SizeOf(integer));
    stream.Free;
    end;

  stream:= TFileStream.create(FileName, fmopenwrite);
  with stream do
  begin
    write(passwort, sizeof(passwort));
    Seek(sizeOf(string), soFromBeginning);
    Laenge:=length(Liste); // stream.write erfordert Variable
    Write(Laenge, SizeOf(laenge));
    for a:= 0 to length(Liste)-1 do
      write(Liste[a], SizeOf(TDatenRec));
    Free;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject); //Das Öffnen findet bei Programmstart statt
var
  stream: TFileStream;
  FileName: string;
  Passwort: string;
  a:integer;
begin
Passwort:='';
FileName:=ExtractFilePath(Application.ExeName) + 'data.dat';
If not FileExists(FileNAme) then exit;
stream:= TFileStream.create(FileName, fmopenread);
with stream do
begin
  Read(Passwort,SizeOf(String));
  read(a, sizeOf(integer));
  SetLength(Liste,a);
  for a:= 0 to length(liste)-1 do
  begin
    Read(Liste[a],SizeOf(TDatenRec));
    ListBox1.Items.Add(Liste[a].Titel);
  end;
end;
stream.Free;
end;


Mache ich einen Denk- oder Unwissenheitsfehler?
Ich weiß unterdessen nicht einmal, ob das Problem beim Speichern oder Öffnen der Datei auftritt.
Würde mich über Hilfe sehr freuen!

Wenn jemand nen Screeni oder das Projekt bräuchte, bitte Bescheid sagen.


Anonymous - So 11.07.04 20:54

:? Vermutlich liegts an den Strings.

Versuch deine Strings mal so zu speichern:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
var
 foo: integer;
...
begin
 foo := length(str);
 stream.write(foo, 4);
 stream.write(@str[1]^, length(str));
end;


Laden dann eben so:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
var
 foo: integer;
...
begin
 stream.read(foo, 4);
 setlength(str, foo);
 stream.read(@str[1]^, length(str));
end;


Delete - Mo 12.07.04 19:13

obbschtkuche hat folgendes geschrieben:

Delphi-Quelltext
1:
2:
3:
stream.write(@str[1]^, length(str));
...
stream.read(@str[1]^, length(str));



Also ich habe generell verstanden, wie Zeiger funktionieren. Nur ich verstehe die [1] in den beiden Zeilen nicht. Mit @ die Addresse von Str ermitteln und mit ^ den Speicherbereich auslesen. Aber warum die [1]?

Edit:
Hab eben den Code getestet. Funktioniert nicht so, wie du ihn geschrieben hast.
Aber so geht es:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
foo:=length(str); //Speichern
write(foo,4);
write(str[1], length(str));
...
read(foo,4); //Laden
setLength(str,foo);
read(str[1], length(str));


Obwohl ich nach wie vor nicht verstehe, was die [1] zu bedeuten hat, funktioniert esnicht ohne sie.


maximus - Di 13.07.04 09:52

Hi, weil strings auch nur pointer sind und bei [1] fängt der eigentlich string an :wink: