Entwickler-Ecke

Dateizugriff - Rekursive Dateisortierung


IronBull - So 21.03.10 00:02
Titel: Rekursive Dateisortierung
Hallo Leute, ich schreibe gerade an einer kleines Datenpaket in einer Datei und würde gerne wissen warum das Sortieren in die Hose geht.

Ich verwende Quicksort um die Datei rekursiv zu sortieren. Dazu verwende ich außerdem eine typisierte Datei in der records abgespeichert werden, die den Interpreten, Titel, Preis und Anzahl verkaufter Titel enthalten. Sprich das Datenpaket ist eine kleine CD Verwaltung.

Die Prozedur rufe ich nacher so
Zitat:
quick(listendatei,1,filesize(listendatei));


Es läuft schief indem das Programm "Keine Rückmeldung" zeigt.


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:
procedure quick(var f:FileT; li,re:integer);
   var i, j:integer;
   dummy,dummy2,mitte:KeyT;
   a, b:char;
    begin

     i:=li;
     j:=re;
     seek(f, ((i+j) div 2));
     read(f, mitte);
     b:=mitte.interpret[0];

    repeat

   seek(f, i);
   read(f, dummy);
   a:=dummy.interpret[0];
   while a < b do
    inc(i);

   seek(f, j);
   read(f, dummy);
   a:=dummy.interpret[0];
   while a > b do
    dec(j);

  if i <= j then
   begin
    seek(f, i);
    read(f, dummy);
    seek(f, j);
    read(f, dummy2);
    write(f, dummy);
    seek(f, i);
    write(f, dummy2);
    inc(i);
    dec(j);
   end;
 until i > j;

 if li<j then
    quick(f, li, j);
 if re>i then
    quick(f, i, re);

 end;


Moderiert von user profile iconChristian S.: Code- durch Delphi-Tags ersetzt


Delete - So 21.03.10 00:26

Während dein Programm den Code in der Schleife abarbeitet, kann es natürlich nicht mehr auf Nachrichten von Windows reagieren. Nach einer Bestimmten Zeit reagiert es dann für Windows nicht mehr. Du hast zwei Möglichkeiten, entweder du lagerst den Code in einen Thread aus, was die sauberere Möglichkeit ist oder du rufst in der Schleife Application.ProcessMessages auf, damit dein Fenster zwischendurch die Nachrichtenschlange abarbeitet.

Allerdings, kann ich mir nicht vorstellen, dass deine Schleife so lange arbeitet, dass Windows denkt, dein Programm würde nicht mehr reagieren. Kann es sein, dass die Abbruchbedingung nicht stimmt und du eine Endlosschleife hast?


SvenAbeln - So 21.03.10 02:13

Dein Quicksort stimmt nicht, du hast dort Endlosschleifen drin.

Falls a < b ist läuft deise Schleife ewig, da beide Variablen innerhalb der Schleife nicht verändert werden.

Delphi-Quelltext
1:
2:
   while a < b do
    inc(i);


bole - So 21.03.10 03:14

Hallo

Soviel ich weiss ist Quick Sort nicht wirklich ideal als externes Sortierverfahren...

Bei internen Sortierverfahren sind alle Daten im Arbeitsspeicher, bei externen sind diese in einer Datei. Für externe Sorts ist Merge Sort zu bevorzugen.

Ich denke bei wenigen Datensätzen ist es performater diese komplett in den Speicher zu laden, zu sortieren und nachher die Datei zu ersetzten.

Siehe auch http://www.stefan-baur.de/cs.algo.sort.html

Gruss

Bole


IronBull - So 21.03.10 09:35

@SvenAbeln

Super, danke für den Hinweis! Habe nun den Code umgeändert und versucht nach Anzahl zu sortieren. Hat astrein funktioniert !

Bleibt ein Problem. Hier unten ist die Stringvariante zu sehen. Bei dieser wird das erste Zeichen verglichen, allerdings funktioniert es nicht. Muss man evtl. wie in C andere Funktionen verwende wie "memcmp()" oder nicht?

Die Interpreten werden nicht geordnet !


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
repeat

   seek(f, i);
   read(f, dummy);
   while dummy.interpret[0] < mitte.interpret[0do
    begin
     inc(i);
     seek(f, i);
     read(f, dummy);
    end;

   seek(f, j);
   read(f, dummy);
   while dummy.interpret[0] > mitte.interpret[0do
    begin
     dec(j);
     seek(f, j);
     read(f, dummy);
    end;
  if i <= j then


P.S.: @Bole danke für den Hinweis. Werde versuchen andere Methode noch einzubauen. Aber erstmal würde ich gerne eine Variante vollständig zum Laufen bringen.

cheers IronBull


alzaimar - So 21.03.10 10:11

Aus Gründen der Übersichtlichkeit würde ich eine Funktion implementieren, die einen bestimmten Datensatz liefert:

Delphi-Quelltext
1:
2:
3:
4:
5:
Function RecordOfFile (aFile : TMyFileOfMyRecord; aRecordIndex : Integer) : TMyRecord;
Begin
  Seek (aFile, aRecordindex);
  Read (aFile, Result);
End;

Anschließend noch eine Funktion, die zwei Datensätze miteinander vergleicht und -1,0 bzw. +1 liefert, je nach Vergleichsergebnis (kleiner, gleich, größer):

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
Function CompareRecords (a,b : TMyRecord) : Integer;
Begin
  if a.interpret < b.interpret then
    Result := -1
  else
    if a.Interpret > b.interpret then
      Result := +1
    else 
      Result := 0;
End;

Und noch eine Funktion, die zwei Records miteinander vertauscht:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
Function ExchangeRecords (aFile : TMyFileOfRecord; aRecordIndex, theOtherRecordIndex : Integer);
Var
  aRecord, theOtherRecord : TMyRecord;

Begin
  aRecord := RecordOfFile (aFile, aRecordIndex);
  theOtherRecord := RecordOfFile (aFile, theOtherRecordIndex);
  seek (aFile, theOtherRecordIndex);
  write (aFile, aRecord);
  seek (aFile, aRecordIndex);
  write (aFile, theOtherRecord);
End;

Und nun Sortieren:

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:
procedure QuickSort(aFile : TMyFileOfRecord; L, R: Integer);
var
  I, J, P: Integer;
  median : TMyREcord;
begin
  repeat
    I := L;
    J := R;
    P := (L + R) shr 1;
    repeat
      median := RecordOfFile (afile, P);
      while CompareRecords (RecordOfFile (afile, I), median) < 0 do Inc(I);
      while CompareRecords (RecordOfFile (afile, J), median) > 0 do Dec(J);
      if I <= J then begin
        ExchangeRecords(aRecord, I, J);
        if P = I then
          P := J
        else if P = J then
          P := I;
        Inc(I);
        Dec(J);
      end;
    until I > J;
    if L < J then QuickSort(aFile, L, J);
    L := I;
  until I >= R;
end;

Die Prozedur ist aus der Classes.Pas - Datei übernommen (TStringList.Quicksort). Ich habe nur die Aufrufe etwas angepasst.

Wie bereits erwäht, gibt es zum externen Sortieren geeignetere Verfahren. Aber am einfachsten ist es, die Datei einzulesen, In-Memory zu sortieren, und wieder abzuspeichern. Das ist natürlich sehr zeitaufwändig, wenn Du viele Einträge hast. Aber ich denke, für deine Sammlung sollte es reichen.

Das wohl beste Verfahren zum externen Sortieren ist übrigens 'MergeSort'.