AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Datenbanken Delphi Speichern eines großen Arrays
Thema durchsuchen
Ansicht
Themen-Optionen

Speichern eines großen Arrays

Ein Thema von Shubit · begonnen am 31. Mär 2010 · letzter Beitrag vom 13. Apr 2010
Antwort Antwort
Seite 3 von 3     123   
Blup

Registriert seit: 7. Aug 2008
Ort: Brandenburg
1.429 Beiträge
 
Delphi 10.4 Sydney
 
#21

Re: Speichern eines großen Arrays

  Alt 6. Apr 2010, 14:26
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;
  Mit Zitat antworten Zitat
Shubit

Registriert seit: 17. Mai 2009
64 Beiträge
 
#22

Re: Speichern eines großen Arrays

  Alt 13. Apr 2010, 09:43
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...
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

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

Re: Speichern eines großen Arrays

  Alt 13. Apr 2010, 10:11
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.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
Blup

Registriert seit: 7. Aug 2008
Ort: Brandenburg
1.429 Beiträge
 
Delphi 10.4 Sydney
 
#24

Re: Speichern eines großen Arrays

  Alt 13. Apr 2010, 12:16
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.
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

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

Re: Speichern eines großen Arrays

  Alt 13. Apr 2010, 12:46
Zitat von Blup:
aber auch nicht mit Strings dynamischer Länge
Die gibt es in dem vorgegebenem Record eh nicht. ,
sonst wäre es mit der "file of" auch nicht gegangen.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
Shubit

Registriert seit: 17. Mai 2009
64 Beiträge
 
#26

Re: Speichern eines großen Arrays

  Alt 13. Apr 2010, 16:19
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.
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 3 von 3     123   


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 05:45 Uhr.
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