Entwickler-Ecke

Dateizugriff - Viele(!) Wahrheitswerte in Datei speichern


P@u1 - Do 15.04.10 18:14
Titel: Viele(!) Wahrheitswerte in Datei speichern
Hallo DF,

wie speichere ich am besten sehr viele (mehrere Millionen) Wahrheitswerte in eine Datei ab?

Habe bisher die Idee gehabt als 1en und 0en in ne text datei zu packen oder evtl die 1en und 0en vorher noch ins 16 oder 10er system umzuwandeln (um die dateigröße zu reduzieren)
Das einlesen wird dadurch aber wahrscheinlich relativ langsam.

Wenn ich das als 1en und 0en in ne text datei schreibe hat die die 8-fache größe von dem was ich erwartet hätte (ich denke er nimmt ein byte für jedes bit...)

Also wie sollte ich am besten vorgehen?

Hoffe das ist hier richtig und kommt nicht in datenbank rein^^

Edit: Wichtig ist auch noch, wie und in was für ein Objekt ich das am besten reinlese.
Edit2: Falls das wichtig ist: Es geht um ca. 26 Millionen Wahrheitswerte


jfheins - Do 15.04.10 18:20

Ein Byte pro Wahrheitswert ist voll okay. Eine 2 Megabyte-Datei ist heutzutage nichts besonderes mehr ;)

Du kannst natürlich auch ein bit pro Wert nehmen, das ist dann halt so klein wie möglich. Aber auch aufwändiger zu lesen und zu schreiben (Klassischer Space vs. Time Tradeoff)


P@u1 - Do 15.04.10 18:22

wie kann ich das denn im bit-format abspeichern?
hätte lieber ca.3 MB größe als 26 MB


jfheins - Do 15.04.10 18:25

Wie hast du die Daten denn vorliegen? Als Boolean Array?


P@u1 - Do 15.04.10 18:27

ich hab sie noch gar nicht vorliegen, ich generiere die selbst, aber boolean-array habe ich bisher benutzt.
wenn es nen besseres format gibt, würde ich evtl auch das nehmen.

Wichtig zu wissen wäre dann noch wie ich das am besten laden kann, also ich kenne bisher leider nur den weg über eine stringlist o.ä. die sachen reinzuladen und dann zu konvertieren, wäre besser es direkt in ein bool-array wieder reinladen zu können

Vielen Dank für deine Hilfe!


jfheins - Do 15.04.10 18:39

Dann geht das ungefähr so:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
procedure SaveToFile(const array)
var Bytearray: Array of Byte;
begin
  setlength(Bytearray, Ceil(length(array)/8));
  for i := 0 to high(arraydo
  begin
    if array[i] then
      Bytearray[i div 8] = Bytearray[i div 8or (1 shl (i mod 8));
  end;
  // Speichere das Bytearray, und die Länge des richtigen Arrays
end;

Das ist jetzt nur so schnell hingeschriben, es geht bestimmt auch noch etwas schneller (Über das bytearray iterieren und immer gleich 8 Bools zusammen speichern) ist dann aber komplizierter, falls die Anzahl deiner Wahrheitswerte nicht glatt durch 8 teilbar ist.


ALF - Do 15.04.10 18:40

Hi, Du kannst doch auch das als 32, 64 bit Werte speichern und beim auslesen wieder die einzelnen bits in Boolean werte lesen. Nachteil, du kannst unter Umständen die Werte in der Text Datei nicht entziffern.

Gruss Alf


P@u1 - Do 15.04.10 18:48

user profile iconjfheins hat folgendes geschrieben Zum zitierten Posting springen:
Dann geht das ungefähr so:

Delphi-Quelltext
1:
2:
      if array[i] then
        Bytearray[i div 8] = Bytearray[i div 8or (1 shl (i mod 8));



kanns sein, dass vor dem gleich ein doppelpunkt fehlt? wenn nicht, dann versteh ich das schon von der syntax nicht, ansonsten versteh ich einige befehle davon auch nicht, aber ich werds mir gleich nochmal genauer angucken

@ALF: Das sagt mir leider nicht so viel, ich weiß weder wie das geht, noch was das bringt :D

Edit: Das größte Problem ist, ich weiß bisher nur wie man dateien als .txt speichert. ich werd aber jetzt mal nen bischen suchen wie das mit anderen formaten geht


jfheins - Do 15.04.10 19:02

ja, das ist eine Zuweisung (also muss ein Doppelpunkt hin) - aber der Code wird so wie er da steht eh nicht kompilieren. Vielmehr ist es ein in Pseudocode gegossener Gedankengang ;)


P@u1 - Do 15.04.10 19:04

wie und in welchem format speicher ich dann denn das byte array am besten ab?
Edit: Ceil und shl sind mir unbekannt


ALF - Do 15.04.10 19:09

im Prinzip das was @jfheins schon geschrieben hat.
Beispiel:
1010001001011001011101011110011 das sind 32 boolean Werte = binärzahl
12113135363 entspricht einem Oktalwert
1361885939 oder Dezimalzahl
512CBAF3 bzw Hexwert

Du siehst damit kannst Du dann platzt in der Datei sparen.
Um das so umzuwandeln muss ich selbst mal schauen, schon lange her :?
Gruss Alf


P@u1 - Do 15.04.10 19:37

heißt 32 bzw 64 bit dann das ich die zahl ins 32er /64er system umwandle? soviele buchstaben gibts dann doch gar nicht :D

das hatte ich am anfang ja auch schon vorgeschlagen,
aber dass wird das einlesen verlangsamen (muss noch konvertiert werden).

Und Soll das dann in einer txt datei gespeichert werden oder besser was anderes?


ALF - Do 15.04.10 19:42

Schau mal hier http://www.delphipraxis.net/topic41913,15.html
und hier ist auch noch was Umwandlung von Dezimalzahlen in andere Zahlensysteme [http://www.delphi-fundgrube.de/faq14.htm]
user profile iconP@u1
Zitat:
aber dass wird das einlesen verlangsamen

Ich bitte Dich, da gibt es komplizierteres, was echt Zeit kostet :wink:

Und abspeichern in einer Textdatei, so kannst Du den String auch gleich wieder nutzen.

Gruss Alf


Martok - Do 15.04.10 19:51

Mal ein anderer Ansatz:

Schreib die Daten so wie sie kommen, von mir aus auch als Bytes, und jage das Ganze durch einen CompressionStream (zlib also).
Solange irgendwelche Muster (=mehrere gleiche Werte o.ä.) drin sind, ist das effektiver als sich unglaublich komplizierte Algorithmen zur Datenverpackung zu bauen.
Zumal du früher oder später dann eh bei Run-Length-Encoding landen würdest, wenn du weiter optimierst ;)


Narses - Do 15.04.10 20:09

Moin!

Suchst du sowas? ;)

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:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
unit BitVektor;

interface

uses
  SysUtils, Classes;

const
  MAGIC = AnsiString('NBVF');

type
  TBitVektor = class(TObject)
  private
    FCapacity: Integer;
    FData: array of LongWord;
    procedure SetCapacity(const Value: Integer);
    function GetBit(const AIndex: Integer): Boolean;
    procedure SetBit(const AIndex: Integer; const Value: Boolean);
  public
    constructor Create(const InitialCapacity: Integer = 32);
    destructor Destroy; override;
    property Capacity: Integer read FCapacity write SetCapacity;
    property Bit[const AIndex: Integer]: Boolean read GetBit write SetBit;
    procedure Clear;
    procedure WriteToStream(AStream: TStream);
    procedure ReadFromStream(AStream: TStream);
    procedure SaveToFile(const AFileName: String);
    procedure LoadFromFile(const AFileName: String);
  end;

implementation

{ TBitVektor }

procedure TBitVektor.Clear;
  var
    i: Integer;
begin
  for i := 0 to High(FData) do
    FData[i] := 0;
end;

constructor TBitVektor.Create(const InitialCapacity: Integer = 32);
begin
  inherited Create;
  SetCapacity(InitialCapacity); // Speicher für Daten holen
end;

destructor TBitVektor.Destroy;
begin
  FData := NIL// Speicher freigeben
  inherited;
end;

function TBitVektor.GetBit(const AIndex: Integer): Boolean;
begin
  Result := (FData[AIndex shr 5and (1 shl (AIndex and 31))) <> 0;
end;

procedure TBitVektor.LoadFromFile(const AFileName: String);
  var
    FS: TFileStream;
begin
  FS := TFileStream.Create(AFileName,fmOpenRead or fmShareDenyWrite);
  try
    ReadFromStream(FS);
  finally
    FS.Free;
  end;
end;

procedure TBitVektor.ReadFromStream(AStream: TStream);
  var
    FileMagic: AnsiString;
    NewCapacity: Integer;
begin
  SetLength(FileMagic,4);
  AStream.Read(PAnsiChar(FileMagic)^,4);
  if (FileMagic = MAGIC) then begin
    AStream.Read(NewCapacity,4);
    SetCapacity(NewCapacity);
    AStream.Read(FData[0],Length(FData)*4);
  end
  else
    raise Exception.Create('Format-Fehler!');
end;

procedure TBitVektor.SaveToFile(const AFileName: String);
  var
    FS: TFileStream;
begin
  FS := TFileStream.Create(AFileName,fmCreate);
  try
    WriteToStream(FS);
  finally
    FS.Free;
  end;
end;

procedure TBitVektor.SetBit(const AIndex: Integer; const Value: Boolean);
begin
  if Value then
    FData[AIndex shr 5] := FData[AIndex shr 5or  (1 shl (AIndex and 31))
  else
    FData[AIndex shr 5] := FData[AIndex shr 5and NOT (1 shl (AIndex and 31));
end;

procedure TBitVektor.SetCapacity(const Value: Integer);
begin
  if (Value > 0then begin
    FCapacity := Value;
    SetLength(FData,((FCapacity +31shr 5));
  end
  else
    raise Exception.Create('TBitVektor.Capacity muss >0 sein!');
end;

procedure TBitVektor.WriteToStream(AStream: TStream);
begin
  AStream.Write('NBVF',4);
  AStream.Write(FCapacity,4);
  AStream.Write(FData[0],Length(FData)*4);
end;

end.
Benutzung im Demo-Projekt.

cu
Narses


P@u1 - Do 15.04.10 20:12

Wie das grundsätzlich mit den Systemen funktioniert wusste ich schon, ich hatte mich nur gefragt, ob 32 bit 32er system und 64 bit 64er system bedeutet.
64er system finde ich nur komisch, weil dafür die buchstaben nicht ausreichen.

ich habe jetzt vorerst die datei nochmal mit den 0en und 1en als txt gelassen, ist dann rund 26 MB groß.
Mit der Datei wollte ich dann ein paar tests machen.
Wenn ich die Datei in eine Stringlist reinlade, dann ist das komischer weise sofort fertig (kann es sein, dass das dann nicht "wirklich" reingeladen wird, sondern nur die stringlist sich abspeichert, von wo er die infos holen soll oder so?
Und wenn ich das dann in ein Array of Boolean reinladen will dauert das sehr lange.
Ich hab testweise erstmal 10000 array werte reingeladen (von ca. 26 Millionen) und das hat schon über eine sekunde gedauert.
Kann man das nicht irgendwie schneller machen bzw. wieso dauert das überhaupt so lange?


Xentar - Do 15.04.10 20:21

Du könntest uns mal zeigen, WIE du die Daten einliest.


Narses - Do 15.04.10 20:22

Moin!

user profile iconP@u1 hat folgendes geschrieben Zum zitierten Posting springen:
ich habe jetzt vorerst die datei nochmal mit den 0en und 1en als txt gelassen, ist dann rund 26 MB groß.
Die Klasse oben macht das mit ca. 3MB und in kaum messbarer Zeit. :D Dafür ist der Zugriff auf einzelne Bits halt nicht so schnell, aber man kann eben nicht alles haben. :nixweiss: Ist für wenig Zugriffe und schnellen I/O gemacht.

cu
Narses


P@u1 - Do 15.04.10 20:29

@narses: ich bin leider noch nicht dazu gekommen, mir die klasse anzugucken, werde ich aber später/morgen noch machen.
Und ich muss darauf sehr oft zugreifen, also ist das wahrscheinlich nicht so besonders gut geeignet.

Jetzt dauert es komischerweise nicht mehr lange, sondern gibt eine zugriffsverletzung
wie ich es einlese:

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:
var
  Form1: TForm1;
  b: Array[1..25000000of Boolean;

procedure TForm1.Button2Click(Sender: TObject);
var
  sl: TStringList;
  i: Longint;
begin
  sl:=TStringList.Create;
  sl.LoadFromFile(OpenDialog1.FileName);
  for i := 1 to 20000000 do
  begin
    try
    if sl[0][i]='1' then
    b[i]:=true
    else
    b[i]:=false;
    except

    ShowMessage(inttostr(i));
    sl.free;
    exit;

    end;
  end;
  sl.Free;

end;


die try/except sachen hab ich eingefügt um rauszufinden, bei welchem wert der fehler kommt, er kommt bei i=1566105
ist da vielleicht der integer zuende oder so? (hab jetzt extra integer durch longint ersetzt)


Narses - Do 15.04.10 20:41

Moin!

user profile iconP@u1 hat folgendes geschrieben Zum zitierten Posting springen:
Und ich muss darauf sehr oft zugreifen, also ist das wahrscheinlich nicht so besonders gut geeignet.
OK, dann nagel doch einfach gleich das Array auf die Platte. :nixweiss:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
var
  b: Array[0..25000000of Boolean;
  FS: TFileStream;

// Stream aufmachen
FS.Write(b[0],Length(b)*SizeOf(Boolean));

FS.Read(b[0],Length(b)*SizeOf(Boolean));
Ist nicht sparsam mit dem Plattenplatz, aber schnell und einfach. :idea:

cu
Narses

//EDIT: Array-Basis auf 0 gesetzt


P@u1 - Do 15.04.10 20:45

Vielen Dank für eure Hilfe!

Ich hab leider keine Ahnung von FileStreams und ich glaube das bei dem code den du gerade geschrieben hast noch was fehlt, es wird ja nirgendwo was aus einer datei geladen.
Und wieso greifst du auf b[0] zu, obwohl b bei 1 anfängt?
ich versteh das nicht^^

Kannst du bitte nochmal kurz erläutern wie das mit dem filestream funktioniert?
danke!


Narses - Do 15.04.10 20:51

Moin!

user profile iconP@u1 hat folgendes geschrieben Zum zitierten Posting springen:
Ich hab leider keine Ahnung von FileStreams
Das ist natürlich... :? schlecht... :|

user profile iconP@u1 hat folgendes geschrieben Zum zitierten Posting springen:
und ich glaube das bei dem code den du gerade geschrieben hast noch was fehlt, es wird ja nirgendwo was aus einer datei geladen.
Klar wird da was geladen, du musst halt noch den FileStream anlegen, der Teil fehlt, das ist richtig (1 Zeile +Resourcen-Schutzblock; Vorlage in der Klasse weiter oben).

user profile iconP@u1 hat folgendes geschrieben Zum zitierten Posting springen:
Und wieso greifst du auf b[0] zu, obwohl b bei 1 anfängt?
ich versteh das nicht^^
Oh, glatt übersehen. :oops: Habe ich auf 0-basiert umgestellt (solltest du auch tun, dynamische Arrays fangen eh immer bei 0 an, dann spart man sich die Index-Transformation in der Compiler-Magic).

user profile iconP@u1 hat folgendes geschrieben Zum zitierten Posting springen:
Kannst du bitte nochmal kurz erläutern wie das mit dem filestream funktioniert?
danke!
Suche in: Delphi-Forum, Delphi-Library TFILESTREAM :lupe: :les: :think: Sorry, aber das haben wir nun wirklich oft genug hier durchgehechelt... :nixweiss:

Wenn ich´s mir recht überlege, bleib lieber bei deiner Stringliste und warte etwas, das ist insgesamt einfacher. :?

cu
Narses


jfheins - Do 15.04.10 20:53

user profile iconNarses hat folgendes geschrieben Zum zitierten Posting springen:
OK, dann nagel doch einfach gleich das Array auf die Platte. :nixweiss:

Das ist ihm doch zu voluminös ;)


P@u1 - Do 15.04.10 21:21

danke für eure hilfe, ich habs inzwischen mit dem filestream prinzipiell hingekriegt, schnelligkeit, dateigröße usw. muss ich noch testen

Edit: es gibt wieder ein problem, ein array of boolean kann wohl nur die maximale länge
Array[1..1033337] of Boolean;
haben, wenn ich die 1033337 um nur 1 erhöhe kommt sofort stack overflow
woran kann das liegen, wie kann ich das ändern?

Edit: Derzeitiger Quelltext:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
procedure TForm1.Button1Click(Sender: TObject);
var
  myArray: Array[1..1033336of Boolean;
  MyStream: TFileStream;
  i: integer;
begin
  randomize;
  for i := 1 to 103336 do
  begin
    if random(2)=1 then
    myArray[i]:=true
    else
    myArray[i]:=false;
  end;

  MyStream:=TFileStream.Create('.\MeineDatei.txt', fmCreate);
  MyStream.Write(MyArray,SizeOf(MyArray));
  myStream.Free;
end;


Hidden - Do 15.04.10 21:48

Hi :)

Der Stack hat nur eine vordefinierte, fixe Größe. Die kannst du aber einstellen.

Wenn du solch ein Array gerade zum ersten Mal anlegst: Wie hast du die Daten denn bisher vorliegen gehabt? Vielleicht ergibt sich ja daraus eine schöne Möglichkeit.
Müssen eigentlich immer alle geladen werden, oder brauchst du stets nur einzelne, kleine Teile?

lg,


P@u1 - Do 15.04.10 21:52

wie vorher schonmal geschrieben, ich habe die daten noch nicht erstellt, weil ich erst gucken wollte in welchem format die am besten sind, ich kann die daten z.B. als array of boolean erstellen.

im späteren einsatz werden immer nur einzelne boolean werte gebraucht, aber relativ häufig.

Edit: Die stack größe steht in den optionen auf $00100000
auf welchen wert sollte ich ihn setzen?


Boldar - Do 15.04.10 22:02

Irgendwie klingt das so, als könnte man das gesamte Konzept noch verbessern.
Was genau macht denn dein programm?


P@u1 - Do 15.04.10 22:20

Also es geht darum wahrscheinlichkeiten/strategien beim poker auszurechnen.

Dazu habe ich gelesen, dass es 2 gute Möglichkeiten gibt:
1. Man macht eine Simulation mit einer begrenzten Anzahl an Zufallswerten
2. Man geht alle (wirklich alle) Möglichkeiten durch

Ich will alle Möglichkeiten durchgehen :D

Nehmen wir an man spielt 1 gegen 1 und man ist beim flop dran (man hat selbst 2 karten (bekannt), der gegner hat 2 karten (unbekannt) und 3 gemeinschaftskarten liegen in der mitte (bekannt).
Also sind noch 4 Karten unbekannt: Der Turn, der River und die zwei Karten des Gegners.
Nun kann man systematisch alle Möglichkeiten durchgehen und gucken, bei wievielen man davon verliert/gewinnt.
Das kann allerdings ein paar Sekunden oder so dauern, fände es besser wenn es schneller/sofort geht.
Deshalb dachte ich ,gehe ich das einmal (wird wahrscheinlich sehr sehr lange dauern) für jede der 25989600 Möglichen Anfangskombinationen (52 über 2) * (50 über 3) durch und bestimme jeweils die Wahrscheinlichkeiten auf Gewinn/Verlust.
Danach wollte ich dann die Ergebnisse abspeichern, zunächst erstmal vereinfacht nur mit Wahrheitswerten, ob die Wahrscheinlichkeit auf Gewinn über oder unter 50% ist.

Dann kann man später die Datei laden, gibt an welche Anfangskombination vorliegt und das Programm sagt einem, ob die Chancen über/unter 50% liegen.

Das soll dann später noch was ausgebaut werden, aber erstmal so zum Konzept^^

Wenn ihr bessere Möglichkeiten seht, bitte her damit, glaube meine ist relativ schlecht :D Evtl wäre es einfach besser einfach die Möglichkeit von oben (ohne Abgleich mit Datei zu optimieren).
Ich wollte erstmal mit der Speicherung der Datei anfangen.

Edit: wenn ich mir das gerade nochmal so durchlese kommt mir das irgendwie lächerlich vor :D 25989600 Möglichkeiten durchzugehen wird wahrscheinlich Jahre (wirklich Jahre) dauern, wenn die Einzelfallbestimmung schon mehrere Sekunden dauert, ich muss mir wohl nen neues Konzept ausdenken


Narses - Do 15.04.10 22:22

Moin!

user profile iconP@u1 hat folgendes geschrieben Zum zitierten Posting springen:
Edit: Die stack größe steht in den optionen auf $00100000
auf welchen wert sollte ich ihn setzen?
Lass die Finger vom Stack und nimm endlich ein dynamisches Array, das liegt auf dem Heap, da ist genug Platz. Dein (lokales) statisches Array passt nicht auf den Stack, das ist der Grund für den Fehler. :idea:

cu
Narses

//EDIT:
user profile iconP@u1 hat folgendes geschrieben Zum zitierten Posting springen:
ich muss mir wohl nen neues Konzept ausdenken
Jup, das würde ich auch sagen.