Delphi-PRAXiS
Seite 3 von 3     123   

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Datenbanken (https://www.delphipraxis.net/15-datenbanken/)
-   -   Delphi Speichern eines großen Arrays (https://www.delphipraxis.net/149837-speichern-eines-grossen-arrays.html)

Blup 6. Apr 2010 14:26

Re: Speichern eines großen Arrays
 
Zitat:

Zitat von Shubit
Nun ich bin für alle Lösungen offen, aber da Datenbanken extra für großen Mengen an Daten ausgelegt sind, sollten sie gerade für längere Vokabellisten sehr gut geeignet sein und wenn sie die restlichen Anforderungen erfüllen, lohnt es sich sicher sich mal damit zu beschäftigen.

Die von dir angeführten Probleme scheinen aber eher mit der Struktur der Anwendung oder der Daten zusammen zu hängen. Wenn alle Daten aus einer Tabelle der Datenbank abgefragt werden, geht das nicht schneller als das Lesen einer Datei. Eine Datenbankanwendung solltest du vieleicht von Grund auf neu entwickeln.

Hier noch Beispielcode zu meinem Lösungsvorschlag mit einer komprimierten Datei:
Delphi-Quellcode:
uses
  Classes, Contnrs;

type
  TVocabulary = class
    Language1: string;
    Language2: string;
    Note: string;
    AddedOn: TDateTime;
    LastTraining: TDateTime;
    Chance: Single;
    Mistakes: Integer;
    Trials: Integer;
    Box: Integer;
  protected
    procedure WriteToStream(AStream: TStream);
    procedure ReadFromStream(AStream: TStream);
  end;

  TVocabularyList = class(TObjectList)
    LanguageName1: string;
    LanguageName2: string;
    Note: string;
  public
    procedure WriteToStream(AStream: TStream);
    procedure ReadFromStream(AStream: TStream);
  end;

  procedure SaveToFile(AList: TVocabularyList; AFilename: string);
  procedure LoadFromFile(AList: TVocabularyList; AFilename: string);

implementation

uses
  SysUtils, Windows, ZLib;

type
  TFileHeader = record
    Ident: array[1..10] of Char;
    procedure WriteToStream(AStream: TStream);
    procedure ReadFromStream(AStream: TStream);
    class operator Explicit(v: TFileHeader): string;
  end;

  TRecordHeader = record
    Ident: string;
    Version: LongWord;
    procedure WriteToStream(AStream: TStream);
    procedure ReadFromStream(AStream: TStream);
  end;

const
  VocabularyIdent_File:  TFileHeader  = (Ident: ('V','o','c','a','b','u','l','a','r','y'));
  VocabularyIdent_Header: TRecordHeader = (Ident: 'Header'; Version: $00010000);
  VocabularyIdent_Items: TRecordHeader = (Ident: 'Items'; Version: $00010000);

procedure WriteStringToStream(AStream: TStream; const AValue: string);
var
  c: LongWord;
begin
  c := Length(AValue);
  AStream.WriteBuffer(c, SizeOf(c));
  AStream.WriteBuffer(PChar(AValue)^, c);
end;

procedure ReadStringFromStream(AStream: TStream; var AValue: string);
var
  c: LongWord;
begin
  AStream.ReadBuffer(c, SizeOf(c));
  SetLength(AValue, c);
  AStream.ReadBuffer(PChar(AValue)^, c);
end;

procedure TFileHeader.WriteToStream(AStream: TStream);
begin
  AStream.WriteBuffer(Ident, SizeOf(Ident));
end;

class operator TFileHeader.Explicit(v: TFileHeader): string;
var
  i: Integer;
begin
  Result := '';
  for i := Low(v.Ident) to High(v.Ident) do
  begin
    if v.Ident[i] = #0 then
      Exit;

    Result := Result + v.Ident[i];
  end;
end;

procedure TFileHeader.ReadFromStream(AStream: TStream);
begin
  AStream.ReadBuffer(Ident, SizeOf(Ident));
end;

procedure TRecordHeader.WriteToStream(AStream: TStream);
begin
  WriteStringToStream(AStream, Ident);
  AStream.WriteBuffer(Version, SizeOf(Version));
end;

procedure TRecordHeader.ReadFromStream(AStream: TStream);
begin
  ReadStringFromStream(AStream, Ident);
  AStream.ReadBuffer(Version, SizeOf(Version));
end;

procedure TVocabularyList.WriteToStream(AStream: TStream);
var
  i, c: LongWord;
begin
  {Datei-Inhalt}
  VocabularyIdent_File.WriteToStream(AStream);
  {Header}
  VocabularyIdent_Header.WriteToStream(AStream);
  WriteStringToStream(AStream, LanguageName1);
  WriteStringToStream(AStream, LanguageName2);
  WriteStringToStream(AStream, Note);
  {Items}
  VocabularyIdent_Items.WriteToStream(AStream);
  c := Count;
  AStream.WriteBuffer(c, SizeOf(c));

  for i := 0 to c - 1 do
    TVocabulary(Items[i]).WriteToStream(AStream);
end;

procedure TVocabularyList.ReadFromStream(AStream: TStream);
{---}
  procedure CheckRecordHeader(const AValue, AReferenz: TRecordHeader);
  begin
    if AValue.Ident <> AReferenz.Ident then
      raise Exception.CreateFmt('ungültiges Dateiformat, ''%s'' erwartet',
        [AReferenz.Ident]);
  end;
{---}
  procedure CheckRecordVersion(const AValue, AReferenz: TRecordHeader);
  begin
    if AValue.Ident <> AReferenz.Ident then
      raise Exception.CreateFmt('ungültige Recordversion ''%s'' V%d.%d',
        [AValue.Ident, HiWord(AValue.Version), LoWord(AValue.Version)]);
  end;
{---}
var
  i, c: LongWord;
  FileHeader: TFileHeader;
  RecordHeader: TRecordHeader;
  v: TVocabulary;
begin
  LanguageName1 := '';
  LanguageName2 := '';
  Note         := '';
  Clear;
  {Datei-Inhalt}
  FileHeader.ReadFromStream(AStream);
  if FileHeader.Ident <> VocabularyIdent_File.Ident then
    raise Exception.CreateFmt('ungültiges Dateiformat, %s erwartet',
      [string(VocabularyIdent_File)]);
  {Header}
  RecordHeader.ReadFromStream(AStream);
  CheckRecordHeader(RecordHeader, VocabularyIdent_Header);
  CheckRecordVersion(RecordHeader, VocabularyIdent_Header);
  ReadStringFromStream(AStream, LanguageName1);
  ReadStringFromStream(AStream, LanguageName2);
  ReadStringFromStream(AStream, Note);
  {Items}
  RecordHeader.ReadFromStream(AStream);
  CheckRecordHeader(RecordHeader, VocabularyIdent_Items);
  CheckRecordVersion(RecordHeader, VocabularyIdent_Items);
  AStream.ReadBuffer(c, SizeOf(c));
  for i := 0 to c - 1 do
  begin
    v := TVocabulary.Create;
    try
      v.ReadFromStream(AStream);
    except
      v.Free;
      raise;
    end;
    Add(v);
  end;
end;

procedure TVocabulary.WriteToStream(AStream: TStream);
begin
  WriteStringToStream(AStream, Language1);
  WriteStringToStream(AStream, Language2);
  WriteStringToStream(AStream, Note);
  AStream.WriteBuffer(AddedOn,     SizeOf(AddedOn));
  AStream.WriteBuffer(LastTraining, SizeOf(LastTraining));
  AStream.WriteBuffer(Chance,      SizeOf(Chance));
  AStream.WriteBuffer(Mistakes,    SizeOf(Mistakes));
  AStream.WriteBuffer(Trials,      SizeOf(Trials));
  AStream.WriteBuffer(Box,         SizeOf(Box));
end;

procedure TVocabulary.ReadFromStream(AStream: TStream);
begin
  ReadStringFromStream(AStream, Language1);
  ReadStringFromStream(AStream, Language2);
  ReadStringFromStream(AStream, Note);
  AStream.ReadBuffer(AddedOn,     SizeOf(AddedOn));
  AStream.ReadBuffer(LastTraining, SizeOf(LastTraining));
  AStream.ReadBuffer(Chance,      SizeOf(Chance));
  AStream.ReadBuffer(Mistakes,    SizeOf(Mistakes));
  AStream.ReadBuffer(Trials,      SizeOf(Trials));
  AStream.ReadBuffer(Box,         SizeOf(Box));
end;

procedure SaveToFile(AList: TVocabularyList; AFilename: string);
var
  stream: TStream;
begin
  stream := TFileStream.Create(AFilename, fmCreate);
  try
    stream := TCompressionStream.Create(clDefault, stream);
    AList.WriteToStream(stream);
  finally
    stream.Free;
  end;
end;

procedure LoadFromFile(AList: TVocabularyList; AFilename: string);
var
  stream: TStream;
begin
  stream := TFileStream.Create(AFilename, fmOpenRead, fmShareDenyWrite);
  try
    stream := TDecompressionStream.Create(stream);
    AList.ReadFromStream(stream);
  finally
    stream.Free;
  end;
end;

Shubit 13. Apr 2010 09:43

Re: Speichern eines großen Arrays
 
Vielen Dank für das ausführlich Beispiel, aber du du lädst beim Öffnen der Vokabelliste doch alle Vokbabeln in einer Objektliste und die wiederrrum liegt irgendwo im Arbeitsspeicher. Ich wollte aber eigentlich nicht alle Vokabeln in den Arbeitsspeicher laden, da es eigentlich nicht nötig sein würde.
Vielmehr würde mir in den meisten Fällen ein einfacher Zugriff auf eine einzelne Vokabl reichen. Vermutlich kann man auch nur einen bestimmten Abschnitt des Streams laden, aber an der Stelle denk ich ist dann die Datenbank die für mich leichtere Methode...

himitsu 13. Apr 2010 10:11

Re: Speichern eines großen Arrays
 
Bloß noch was zum Array, also wenn man die Datenbank nicht möchte.

Delphi-Quellcode:
TVocabulary = record
  Language1: String[100];
  Language2: String[100];
  Note: String[100];
  AddedOn: TDateTime;
  LastTraining: TDateTime;
  Chance: Single;
  Mistakes: Integer;
  Trials: Integer;
  Box: Integer;
end;
TVocabularyArray = Array[0..0] of TVocabulary;
PVocabularyArray = ^TVocabularyArray;
Die Größe des "reellen Arrays wird dann über Dateigröße/Recordgröße berechnet. (ein Length/High gibt es hier ja nicht)

Und jetzt via MMF (Memory Mapped File) die Datei laden und einfach den Array-Pointer drüberlegen.
Somit liegt der Großteil der Datei nicht im RAM und es werden nur die Teile automatisch in den Speicher gemappt, auf welche zugegriffen wird.
Es belegt zwar im Programm immernoch etwa genauso viel "virtuellen" Speicher, wie das "normale" dynamische Array, aber im Windows wird nur ein Bruchteil das Speichers verbraucht, da große Teile einfach in der Datei verbleiben können.

Blup 13. Apr 2010 12:16

Re: Speichern eines großen Arrays
 
Zitat:

Zitat von Shubit
Vielen Dank für das ausführlich Beispiel, aber du du lädst beim Öffnen der Vokabelliste doch alle Vokbabeln in einer Objektliste und die wiederrrum liegt irgendwo im Arbeitsspeicher. Ich wollte aber eigentlich nicht alle Vokabeln in den Arbeitsspeicher laden, da es eigentlich nicht nötig sein würde.
Vielmehr würde mir in den meisten Fällen ein einfacher Zugriff auf eine einzelne Vokabl reichen. Vermutlich kann man auch nur einen bestimmten Abschnitt des Streams laden, aber an der Stelle denk ich ist dann die Datenbank die für mich leichtere Methode...

Wenn dein Projekt tatsächlich nicht den Zugriff auf alle Elemente braucht, ist natürlich eine Datenbank besser.
Allerdings hätte es dann auch keine Geschwindigkeitsprobleme mit dem "File of ..." gegeben (ein Memory Mapped File wäre eine elegantere Variante, funktioniert aber auch nicht mit Strings dynamischer Länge).
Deshalb befürchte ich, daß die Geschwindigkeitsprobleme durch den Einsatz eines Datenbankservers nicht gelöst werden. Die Daten werden vieleicht nicht in den Arbeitsspeicher deiner Anwendung geladen, aber auf Grund der Art deiner Datenabfragen in den Arbeitsspeicher des Datenbankservers. Aber berichte uns von deinen Erfahrungen, sobald dein Projekt soweit ist.

himitsu 13. Apr 2010 12:46

Re: Speichern eines großen Arrays
 
Zitat:

Zitat von Blup
aber auch nicht mit Strings dynamischer Länge

Die gibt es in dem vorgegebenem Record eh nicht. :stupid:,
sonst wäre es mit der "file of" auch nicht gegangen.

Shubit 13. Apr 2010 16:19

Re: Speichern eines großen Arrays
 
wenn du meinen ersten Post gelesen hättest, wüsstest du das mich dies durchaus stört. Dynamische Array wären mir deutlich lieber ...

Also die Datenstruktur muss im Grunde genommen 3 Aufgaben erfüllen:
1. Vokabelabfrage: Dazu würde ich bei der Datenbank den aktuellen Eintrag einmal in ein TVocabulary laden (mit dynamischen arrays) und beim Wechsel zur nächsten Vokabel dann wieder von TVocabulary in die Datenbank speichern.
2. Vokabelliste bearbeiten: Hier geht es im Wesentlichen darum einen Teil der Vokabeln in einer Listbox oder einem Grid (überleg ich mir dann noch) darzustellen und diese dann über zusätzliche Editfelder zu bearbeiten. Dabei wird in der Regel auch immer nur eine Vokabel geladen (außer beim löschen von mehreren Eintrag gleichzeitig).
3. Statistik: Einfach nur ein paar durchschnittliche Werte (Anzahl Fehler etc.), könnte man entweder jedes mal neu bestimmen oder halt entsprechende Variablen mitführen die man dann immer schön aktuell halten muss.


Alle Zeitangaben in WEZ +1. Es ist jetzt 11:04 Uhr.
Seite 3 von 3     123   

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz