Entwickler-Ecke

Dateizugriff - Lesen mit TFileStream liefert Müll


drstar - Di 17.10.17 12:05
Titel: Lesen mit TFileStream liefert Müll
So, neues Problem, nachdem ich die Datenbank-Problematik nun beseitigt habe.

Ich speichere die Inhalte der Objekte in eine Textdatei, das funktioniert auch, sind ohnehin nur Zahlen und Strings. Nur beim Einlesen kommt Müll raus - ich speichere 4 Strings hintereinander, jeweils vorgestellt mit Längenangabe. TFileStream liefert mir beim ersten String die ersten 3 Zeichen korrekt, die übrigen nimmt er von irgendwoher aus der Datei, als ob er mittendrin rumspringt.

Folgender Code:


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:
type TFileStreamExt  = class(TFileStream)
       procedure Delete(Pos, len: integer);
       procedure WriteString(aStr: string);
       function  ReadString: string;
     end;

procedure TFileStreamExt.Delete(Pos: Integer; len: Integer);
begin
  //spielt hier keine Rolle, nur der Vollständigkeit halber aufgeführt
end;

function TFileStreamExt.ReadString: string;
var
   aStrLen: Integer;
begin
   Read(aStrLen, SizeOf(Integer));
   SetLength(Result, aStrLen);
   Read(pointer(Result)^, aStrLen * SizeOf(AnsiChar));
end;

procedure TFileStreamExt.WriteString(aStr: string);
var
   aStrLen: Integer;
begin
   aStrLen := Length(aStr);
   Write(aStrLen, SizeOf(Integer));
   Write(Pointer(aStr)^, aStrLen * SizeOf(Char));
end;



Im Programm nutze ich TFileStreamExt wie folgt:


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:
  procedure ReadKlient(var K: TKlientExt);
  var i : integer;
      L: integer;
  begin
    if not assigned(K)
    then
      K.create;
    K.Position := Stream.Position -4;   // soll so sein, weil der Aufruf dieser Methode aufgrund eines Integers erfolgt, der vorher eingelesen wurde, beim Schreiben aber dem Objekt zugeschrieben wird
    Stream.Read(K.Size, SizeOf(K.Size));
    Stream.Read(K.Status, SizeOf(K.Status));
    Stream.Read(K.Index, SizeOf(K.Index));
    K.Vorname := Stream.ReadString;
    K.Nachname := Stream.ReadString;
    K.Strasse := Stream.ReadString;
    K.Ort := Stream.ReadString;
    K.isSaved := true; 
  end;

procedure WriteKlient(K: TKlientExt);
  var L: Integer;
      OldPos, OldSize: integer;
      StartPos, EndPos : integer;
  const z : integer = 1;
  begin
    OldPos := K.Position;
    OldSize := K.Size;
    if (K.isSaved = false) or (K.isChanged = true)
    then
    begin
      StartPos := Stream.Position;
      Stream.Write(z, SizeOf(z));
      Stream.Write(K.Size, SizeOf(K.Size));
      Stream.Write(K.Status, SizeOf(K.Status));
      Stream.Write(K.Index, SizeOf(K.Index));
      Stream.WriteString(K.Vorname);
      Stream.WriteString(K.Nachname);
      Stream.WriteString(K.Strasse);
      Stream.WriteString(K.Ort);
      K.isSaved := true;
      EndPos := Stream.Position;
      K.Size := EndPos - StartPos;
      Stream.Position := StartPos +4;
      Stream.Write(K.Size, SizeOf(K.Size));
      if K.isChanged = false
      then
      begin
        K.Position := StartPos;
      end;
      K.Size := EndPos - StartPos;
      Stream.Position := EndPos;
    end;
    if K.isChanged = true
    then
    begin
      Stream.Delete(OldPos, OldSize);
      DB_change(OldPos, OldSize);
      K.isSaved := true;
      K.isChanged := false;
      K.Position := StartPos;
    end;
  end;


Wenn ich nun meinen Vornamen, Nachnamen, Strasse und Ort speichere, sieht alles korrekt aus - lese ich meinen Vornamen ein, stimmen die ersten 3 Zeichen, die übrigen 3 Zeichen gehören aber zu anderen Strings (ein Zeichen wohl zu meinem Nachnamen, und 2 zu meiner Strasse). Für mich wirkt es, als würde Filestream nicht hintereinander die Zeichen einlesen, was mache ich falsch? Die Länge der Strings ist korrekt gespeichert, das habe ich überprüft - bringt mir aber nichts, weil er nur die Länge des ersten Strings korrekt erfaßt, eben weil FileStream anschließend wild herumspringt und dann die Längenangabe des nächsten Strings nicht mehr trifft.


baka0815 - Di 17.10.17 13:06

Sieht nach einem Unicode-Problem aus.

ReadString() macht Read(pointer(Result)^, aStrLen * SizeOf(AnsiChar)); wohingegen du beim Schreiben Write(Pointer(aStr)^, aStrLen * SizeOf(Char)); verwendest.

Stell mal auch das Lesen um auf Read(pointer(Result)^, aStrLen * SizeOf(Char)); und gucke was passiert.


drstar - Di 17.10.17 13:11

waaaaaahhhh ich glaube es nicht.

Ich bin im Embarcadero-Hinweis um einen Absatz verrutscht - es lag tatsächlich am Unicode. Danke, ich hab schon hin- und herüberlegt, aber den Fehler habe ich nicht gesehen. Danke, es geht perfekt


Delete - Di 17.10.17 13:12

- Nachträglich durch die Entwickler-Ecke gelöscht -


drstar - Di 17.10.17 13:15

user profile iconFrühlingsrolle hat folgendes geschrieben Zum zitierten Posting springen:
Unicode, bei Delphi 6, ernsthaft? Char oder AnsiChar, wird auf AnsiChar hinauslaufen.


Öhm ich aktualisiere mal meine Info... Delphi 10.1


Delete - Di 17.10.17 13:18

- Nachträglich durch die Entwickler-Ecke gelöscht -


haentschman - Di 17.10.17 14:31

Moin... :P

Eigentlich müßte sich eine moderne IDE bei:
K.isChanged = true
...sich weigern zu starten. :zwinker: (Scherz den du mal nachlesen solltest)


baka0815 - Mi 18.10.17 08:43

user profile iconhaentschman hat folgendes geschrieben Zum zitierten Posting springen:
Moin... :P

Eigentlich müßte sich eine moderne IDE bei:
K.isChanged = true
...sich weigern zu starten. :zwinker: (Scherz den du mal nachlesen solltest)


Guter Hinweis, du solltest tatsächlich nur if K.isChanged statt mit if K.isChanged = true abfragen.
Für den False-Fall dann entsprechend mit if not K.isSaved.


drstar - Mi 18.10.17 13:45

user profile iconbaka0815 hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconhaentschman hat folgendes geschrieben Zum zitierten Posting springen:
Moin... :P

Eigentlich müßte sich eine moderne IDE bei:
K.isChanged = true
...sich weigern zu starten. :zwinker: (Scherz den du mal nachlesen solltest)


Guter Hinweis, du solltest tatsächlich nur if K.isChanged statt mit if K.isChanged = true abfragen.
Für den False-Fall dann entsprechend mit if not K.isSaved.


Alte Angewohnheit von mir, und falsch ist es ja zum Glück nicht.


haentschman - Mi 18.10.17 14:22

Zitat:
und falsch ist es ja zum Glück nicht.

:mahn: ..eben nicht.
Siehe: http://www.delphipraxis.net/57121-ueber-den-umgang-mit-boolean.html
Zitat:
Der Vergleich "BoolVar=True" kann sogar kräftig in die Hose gehen!


drstar - Mi 18.10.17 23:33

user profile iconhaentschman hat folgendes geschrieben Zum zitierten Posting springen:
Zitat:
und falsch ist es ja zum Glück nicht.

:mahn: ..eben nicht.
Siehe: http://www.delphipraxis.net/57121-ueber-den-umgang-mit-boolean.html
Zitat:
Der Vergleich "BoolVar=True" kann sogar kräftig in die Hose gehen!


OK, das war mir neu, wieder was gelernt. Ich ging bislang immer davon aus, daß der Test auf true oder eben false korrekt ist; daß nur der Test auf false immer das richtige Ergebnis liefert, merke ich mir aber und werde meinen Programmcode mal entsprechend anpassen.


haentschman - Do 19.10.17 08:01

Moin... 8)
Zitat:
OK, das war mir neu, wieder was gelernt

...das lob ich mir. :zwinker:

PS: Wenn du richtig programmieren willst, dann hört das nie auf. :zwinker:


baka0815 - Do 19.10.17 12:29

Zum Tutorial: Die Zuweisung BoolVar := not (IntVar = 5); würde ich als BoolVar := (IntVar <> 5); schreiben, das ist deutlich klarer.