[allgemeinen Dateizugriff] filestream mit string als text
spacer
Autor Nachricht
gerd8888
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 56



BeitragVerfasst: So 15.01.12 18:09 
Betrifft: allgemeinen Dateizugriff
Hallo,

ich habe eine Frage zu dem filestream.
1. Ich will den Text (hier die Zahlen 0-5) mit einer gewissen Textlaenge (st_laenge) in test.dat speichern.
Das funktioniert aber nur teilweise. Wenn ich die st_laenge=4 auf 4 Beschraenke steht in der test.dat "1 2 3 4 "
Genau so wie ich es haben will.
Aber bei st_laenge=8 kommt das hier: "0 1 3 2 3 4 3 5 "
Es sind 2 "3" Zahlen nicht richtig platziert.
Hier den gesamten Code.
Wichtig ist der Button1, da schreibt er das in die Datei rein. Die Datei hat Ansi-Codierung.
Die Datei test.dat muss vorher erstellt werden.
(Button2 liest aus und Button3 ueberschreibt.)

ausblenden volle Höhe Delphi-Quelltext markieren
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:
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;



type
TForm1 = class(TForm)
Button1: TButton;
Label1: TLabel;
ListBox1: TListBox;
Button2: TButton;
Button3: TButton;
Label2: TLabel;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}
type Tdaten=string;
const st_laenge=8;

procedure SaveStringToFile (Filename, SaveString: tdaten; npos:integer);
var
fs: TFileStream;
begin
fs := TFileStream.Create (Filename, fmopenreadwrite);
try
if npos=-1 then begin fs.size:=0; npos:=0; end else
if npos=0 then begin
fs.Position:=fs.size;
end else fs.Position:=(npos-1)*st_laenge;
form1.listbox1.items.add(inttostr(fs.Size));
if SaveString <> '' then
fs.Write (SaveString[1]{PChar(savestring)^}, st_laenge{Length (SaveString)});
finally
fs.Free;
end;
end;

procedure LoadStringFromFile(Filename: string; npos:integer; var LoadString: tdaten);
var
fs: TFileStream;
begin
fs := TFileStream.Create (Filename, fmOpenRead or fmShareDenyNone);
try
fs.Position:=(npos-1)*st_laenge;
SetLength (LoadString, fs.size);
//form1.listbox1.items.add(inttostr(fs.Size)+' V ');
if fs.size>0 then
fs.Read (LoadString[1],fs.size);
finally
fs.Free;
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var data:tdaten;
x:integer;
begin
data:='0';
savestringtofile(ExtractFilePath(Application.ExeName) + 'test.dat',data,-1);
for x:=1 to 5 do begin
data:= inttostr(x);
savestringtofile(ExtractFilePath(Application.ExeName) + 'test.dat',data,0);
end;

end;

procedure TForm1.Button2Click(Sender: TObject);
var data:tdaten;
x:integer;
begin
listbox1.Clear;
for x:=1 to 4 do begin
Loadstringfromfile(ExtractFilePath(Application.ExeName) + 'test.dat',x,data);
listbox1.Items.Add(data);
end;
end;

procedure TForm1.Button3Click(Sender: TObject);
var x:integer;
data:tdaten;
begin
x:=2;
data:='7';
savestringtofile(ExtractFilePath(Application.ExeName) + 'test.dat',data,2);
end;

end.
 
Antworten mit Zitat Beitrag melden
Private Nachricht sendenPosting in privater Nachricht zitieren
Werbung ausblenden? Dann registriere Dich kostenlos. Weitere Gründe für eine Registrierung.


Werbung ausblenden? Dann registriere Dich kostenlos. Weitere Gründe für eine Registrierung.
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 15843
Erhaltene Danke: 742

XP, W7 x64 (Chrome, IE9, FF), Debian, (OSX 10.7)
RAD XE 2, Java (NB), C++, C# (VS 2010), JS/HTML, PHP, Lazarus
BeitragVerfasst: So 15.01.12 18:43 
Ohne jetzt geschaut zu haben was da schief geht:
Was hast du eigentlich vor? Denn so wirklich sinnvoll sieht das nicht aus was du da machst. :gruebel:
 
Antworten mit Zitat Beitrag melden
Private Nachricht sendenPosting in privater Nachricht zitieren
Andreas L.
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starhalf offtopic starofftopic starofftopic star
Beiträge: 1671
Erhaltene Danke: 14

Windows Vista Home Premium 32Bit
RAD Studio 2009 Pro
BeitragVerfasst: So 15.01.12 18:58 
Warum nicht gleich eine TStringList?

ausblenden Delphi-Quelltext markieren
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
var
sl: TStrings;
begin
sl := TStringList.Create;
try
sl.Text := '1 2 3 4 5';
sl.SaveToFile('C:\datei.dat');
finally
FreeAndNil(sl);
end;
end;
 
Antworten mit Zitat Beitrag melden
Private Nachricht sendenPosting in privater Nachricht zitieren
gerd8888 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 56



BeitragVerfasst: So 15.01.12 19:12 
Die ganzen Daten habe ich bereits auf TStringliste. Aber das will ich nicht. Ich muesste ja alles vorher komplett in den Arbeitsspeicher laden.
Ich moechte meine Daten, die jeweils aus st_laenge (8) sind, in eine Datei abspeichern und wieder aufrufen. Und zwar so, dass ich das ganze in einer Datei nicht binär, sondern als Text (aus Sicherheitsgruenden) vorfinde.
Ich habe bloss Probleme, die Strings so zu speichern, dass ich sie auch wieder lesen kann. Ansonsten wuerde es schon gehen. Nur teilweise habe ich Probleme damit.

Moderiert von user profile iconNarses: Beiträge zusammengefasst

einen filestream braucht man, um daten abzuspeichern und wieder auszulesen. jaenicke, genau das habe ich vor, nur so, dass in der datei ein leserlicher text vorzufinden ist. Also Datensaetze, die einen String von der Laenge 8 haben und das 100000-mal. Genau das habe ich vor.
 
Antworten mit Zitat Beitrag melden
Private Nachricht sendenPosting in privater Nachricht zitieren
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 15843
Erhaltene Danke: 742

XP, W7 x64 (Chrome, IE9, FF), Debian, (OSX 10.7)
RAD XE 2, Java (NB), C++, C# (VS 2010), JS/HTML, PHP, Lazarus
BeitragVerfasst: So 15.01.12 19:25 
Eigentlich halte ich davon zwar nichts, aber für den Zweck bietet sich ein Record an:
ausblenden Delphi-Quelltext markieren
1:
2:
3:
4:
5:
6:
7:
type
TExample = packed record
Contents: array[0..7] of Char;
end;

var
MyFile: file of TExample;
Da können natürlich auch mehrere Bestandteile im Record stehen und mit einem Klassenoperator kann man auch sehr gut damit arbeiten, wenn man den Inhalt der Einträge z.B. als ganzen String betrachten will.

Zudem kann man darin auch Eigenschaften haben, die automatisch an die richtigen Stellen im Array geschrieben werden usw., falls das nötig ist.
 
Antworten mit Zitat Beitrag melden
Private Nachricht sendenPosting in privater Nachricht zitieren
gerd8888 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 56



BeitragVerfasst: So 15.01.12 19:38 
den recorde im filestream hernehmen?
Ich habe keine Probleme die Daten einzulesen und auszulesen mit assign(file) usw.
Mir geht es ehrlich gesagt auch nur ein bisschen am Rumspielen vom dem streamfile.
Und waere gerne daran rein theoretisch interessiert.
Vielleicht ist es so am besten ausgedrueckt.

Moderiert von user profile iconNarses: Beiträge zusammengefasst

Das duerfte das Problem sein.
type tdaten=ansistring;

und wie welcher codierung arbeitet man, wenn man mit string arbeitet?
Zu solchen speziellen Fragen gibt es im Internet fast keine Hilfe.
 
Antworten mit Zitat Beitrag melden
Private Nachricht sendenPosting in privater Nachricht zitieren
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 15843
Erhaltene Danke: 742

XP, W7 x64 (Chrome, IE9, FF), Debian, (OSX 10.7)
RAD XE 2, Java (NB), C++, C# (VS 2010), JS/HTML, PHP, Lazarus
BeitragVerfasst: So 15.01.12 20:54 
user profile icongerd8888 hat folgendes geschrieben Zum zitierten Posting springen:
Das duerfte das Problem sein.
type tdaten=ansistring;
Da du im Profil deine Delphiversion nicht ausgefüllt hast, kann ja niemand wissen, ob du eine Unicodeversion von Delphi hast. :nixweiss:
Wenn du Delphi 2009 oder höher hast, brauchst du das, wenn du eine Ansidatei schreiben willst (warum?).

Dein Problem liegt aber ganz woanders:
ausblenden Delphi-Quelltext markieren
1:
      fs.Write (SaveString[1]{PChar(savestring)^}, st_laenge{Length (SaveString)});
Du schreibst immer z.B. 8 Byte, auch wenn so viel in dem String gar nicht drin ist (da steht ja z.B. nur eine Zahl drin). Dementsprechend sind es zufällige Daten, die du in die Datei schreibst.

Aber ich merke schon, du magst umständliche Wege. Dann mal ein Beispiel (funktioniert nur mit Ansi, für Unicodedateien fehlt noch ein bisschen was):
ausblenden volle Höhe Delphi-Quelltext markieren
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:
type
TExample = packed record
strict private
var
FContents: array[0..7] of AnsiChar;
function GetChar1: AnsiChar;
procedure SetChar1(const Value: AnsiChar);
procedure SetContents2(const Value: AnsiString);
function GetContents2: AnsiString;
public
procedure SaveToStream(AStream: TStream);
procedure LoadFromStream(AStream: TStream);
property Char1: AnsiChar read GetChar1 write SetChar1;
property Contents2: AnsiString read GetContents2 write SetContents2;
end;

TForm181 = class(TForm)
btnSave: TButton;
btnLoad: TButton;
procedure btnSaveClick(Sender: TObject);
procedure btnLoadClick(Sender: TObject);
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;

var
Form181: TForm181;

implementation

{$R *.dfm}

{ TExample }

function TExample.GetChar1: AnsiChar;
begin
Result := FContents[0];
end;

procedure TExample.SetChar1(const Value: AnsiChar);
begin
FContents[0] := Value;
end;

procedure TExample.SetContents2(const Value: AnsiString);
var
ContentsSize: Integer;
begin
ContentsSize := SizeOf(FContents) - SizeOf(FContents[0]);
ZeroMemory(@FContents[1], ContentsSize);
CopyMemory(@FContents[1], PAnsiChar(Value), Min(Length(Value), ContentsSize));
end;

function TExample.GetContents2: AnsiString;
var
ContentsSize: Integer;
begin
ContentsSize := SizeOf(FContents) - SizeOf(FContents[0]);
SetLength(Result, ContentsSize);
Result := Copy(FContents, 2, ContentsSize);
end;

procedure TExample.LoadFromStream(AStream: TStream);
begin
AStream.ReadBuffer(FContents, SizeOf(FContents));
AStream.Seek(Length(sLineBreak), soCurrent);
end;

procedure TExample.SaveToStream(AStream: TStream);
begin
AStream.WriteBuffer(FContents, SizeOf(FContents));
AStream.WriteBuffer(sLineBreak, Length(sLineBreak));
end;

procedure TForm181.btnSaveClick(Sender: TObject);
var
MyFile: TFileStream;
CurrentEntry: TExample;
i: Integer;
begin
MyFile := TFileStream.Create(GetCurrentDir + '\test.txt', fmCreate);
try
for i := 0 to 5 do
begin
CurrentEntry.Char1 := AnsiChar(IntToStr(i)[1]);
CurrentEntry.Contents2 := 'test';
CurrentEntry.SaveToStream(MyFile);
end;
finally
MyFile.Free;
end;
end;

procedure TForm181.btnLoadClick(Sender: TObject);
var
MyFile: TFileStream;
CurrentEntry: TExample;
i: Integer;
begin
MyFile := TFileStream.Create(GetCurrentDir + '\test.txt', fmOpenRead);
try
while MyFile.Position < MyFile.Size do
begin
CurrentEntry.LoadFromStream(MyFile);
ShowMessage(CurrentEntry.Char1);
ShowMessage(CurrentEntry.Contents2);
end;
finally
MyFile.Free;
end;
end;
Einfach nur mit einem Knopf zum Speichern von 6 Einträgen und einen zum Laden.
 
Antworten mit Zitat Beitrag melden
Private Nachricht sendenPosting in privater Nachricht zitieren
gerd8888 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 56



BeitragVerfasst: So 15.01.12 22:52 
Den String bei meiner Version, kann ich ja hinterher auch mit Leerzeichen fuellen, so dass das kein Problem mehr darstellt.
Jedenfalls funktioniert meine Version jetzt einwandfrei. Auf eine Kleinigkeit beim loadstringfromfile moechte ich noch hinweisen.
ausblenden Delphi-Quelltext markieren
1:
2:
3:
SetLength (LoadString, fs.size);
if fs.size>0 then
fs.Read (LoadString[1],fs.size);

Das fs.size muss natuerlich in fs.read durch st_laenge ersetzt werden. Das sollte aber klar sein.

Dein Code habe ich gerade getestet und funktioniert einwandfrei. Besten Dank für Deine Mühe. Wenn ich das richtig sehe, braucht man nicht unbedingt AnsiString und AnsiChar. Man kommt auch nur mit einem aus. Ich glaube du wolltest das nur mal zeigen.
Die 2 Zeilen sind mir noch unklar und der Hokus-Pokus mit dem linebreak ist mir auch neu.
ausblenden Delphi-Quelltext markieren
1:
2:
ZeroMemory(@FContents[1], ContentsSize);
CopyMemory(@FContents[1], PAnsiChar(Value), Min(Length(Value), ContentsSize));

Ich werde mich morgen mit Deinem source code noch etwas beschaeftigen.

Und ja Delphi 2009 benutze ich.

Moderiert von user profile iconNarses: Delphi-Tags hinzugefügt
 
Antworten mit Zitat Beitrag melden
Private Nachricht sendenPosting in privater Nachricht zitieren
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 15843
Erhaltene Danke: 742

XP, W7 x64 (Chrome, IE9, FF), Debian, (OSX 10.7)
RAD XE 2, Java (NB), C++, C# (VS 2010), JS/HTML, PHP, Lazarus
BeitragVerfasst: So 15.01.12 23:14 
user profile icongerd8888 hat folgendes geschrieben Zum zitierten Posting springen:
Wenn ich das richtig sehe, braucht man nicht unbedingt AnsiString und AnsiChar. Man kommt auch nur mit einem aus. Ich glaube du wolltest das nur mal zeigen.
Richtig, ich wollte zeigen, dass du auf diese Weise die Daten über Eigenschaften in das Array stecken und daraus lesen kannst.

Ich persönlich benutze allerdings eher Klassen und schreibe die einzelnen Bestandteile in SaveToStream einzeln in den Stream.

user profile icongerd8888 hat folgendes geschrieben Zum zitierten Posting springen:
Die 2 Zeilen sind mir noch unklar und der Hokus-Pokus mit dem linebreak ist mir auch neu.
ZeroMemory(@FContents[1], ContentsSize);
CopyMemory(@FContents[1], PAnsiChar(Value), Min(Length(Value), ContentsSize));
Ich schreibe zuerst Nullzeichen in die 7 Bytes ab dem zweiten Byte im Array und schreibe dann je nach Länge des übergebenen Strings in die Bytes ab dem zweiten die übergebenen Zeichen.
 
Antworten mit Zitat Beitrag melden
Private Nachricht sendenPosting in privater Nachricht zitieren
home home