Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Problem mit TFileStream.Read (https://www.delphipraxis.net/150007-problem-mit-tfilestream-read.html)

Lumpiluk 5. Apr 2010 10:42


Problem mit TFileStream.Read
 
Frohe Ostern!

Tut mir Leid, falls der Titel nicht allzu aussagekräftig sein sollte, aber was besseres fällt mir dazu nicht ein:

Delphi-Quellcode:
procedure TMazeEditor.LoadBinClick(Sender: TObject);
var
  MazeStream: TStream;
  x,y, StrLength, dummyInt: Integer;
  TmpStr: String;
  TmpByte: Byte;
begin
  if OpenDialog1.Execute then
  begin
    try
      MazeStream := TFileStream.Create(OpenDialog1.FileName, fmOpenRead);
      MazeStream.Read(StrLength, SizeOf(StrLength));
      MazeStream.Read(TmpStr, StrLength);
      if TmpStr = 'irgendein bestimmter String...' then
      begin
So sieht der Anfang der Prozedur im einen Projekt aus (ein Editor für ein kleines Spiel, womit ich gerade anfange), und so im eigentlichen Spiel:
Delphi-Quellcode:
procedure TMazeGame.LoadMazeFromFile(AFile: String);
var
  MazeStream: TStream;
  x,y, StrLength, dummyInt: Integer;
  TmpStr: String;
  TmpByte: Byte;
begin
  try
    MazeStream := TFileStream.Create(AFile, fmOpenRead); //der Pfad scheint richtig zu sein
    MazeStream.Read(StrLength, SizeOf(StrLength)); //StrLength ist hiernach merkwürdigerweise nur 4
    MazeStream.Read(TmpStr, StrLength);
    if TmpStr = 'irgendein bestimmter String...' then //hier kommt der Fehler
    begin
Im Editor funktioniert das Laden perfekt, allerdings kommt im eigentlichen Spiel beim Laden derselben Datei bei der If-Abfrage diese Exception:
"Im Projekt LS_Maze.exe ist eine Exception der Klasse EAccessViolation mit der Meldung 'Zugriffsverletzung bei Adresse 00405D49 in Modul 'LS_Maze.exe'. Lesen von Adresse FFFFFFFC' aufgetreten."
Seltsam finde ich auch, dass nachher trotz "try" eine Fehlermeldung kommt.
Das Programm nochmal extra mit Administratorrechten zu starten hat auch nichts gebracht.
Woran könnte das liegen? (Ich schätze, ich habe wieder irgendwas Einfaches übersehen ;) )

SirThornberry 5. Apr 2010 10:48

Re: Problem mit TFileStream.Read
 
es ist reines glück das im Editor kein Fehler kommt. Anstelle von
Delphi-Quellcode:
MazeStream.Read(TmpStr, StrLength);
muss es
Delphi-Quellcode:
MazeStream.Read(TmpStr[1], StrLength);
heißen. Denn ein String, ein dynamisches Array etc. sind etwas mehr als nur Speicherplatz. Dahinter verbirgt sich eigentlich ein Pointer auf die eigenltichen Daten mit Referenzzählung, Längenangabe etc.
Mit TmpStr[1] gibst du sozusagen das erste Zeichen des Strings an und somit wird ab diesem Byte der String beschrieben und nicht die Stelle wo der Pointer etc. sonst ist.
Im übrigen musst du vorher natürlich auch die Größe des Strings setzen!

Delphi-Quellcode:
SetLength(TmpStr, StrLength);
MazeStream.Read(TmpStr[1], StrLength);

Lumpiluk 5. Apr 2010 13:07

Re: Problem mit TFileStream.Read
 
Danke, jetzt klappt's! :)

Zitat:

Zitat von SirThornberry
Delphi-Quellcode:
SetLength(TmpStr, StrLength);
MazeStream.Read(TmpStr[1], StrLength);

So kam allerdings nur die Hälfte des Strings an, aber dank dieser seite habe ich es jetzt so hinbekommen (vielleicht hilft es noch irgendwem...):
Delphi-Quellcode:
procedure TMazeEditor.SaveBinClick(Sender: TObject);
var
  MazeStream: TStream;
  StrLength: Integer;
  ValidationString: String;
begin
  if SaveDialog1.Execute then
  begin
    try
      MazeStream := TFileStream.Create(SaveDialog1.FileName,fmCreate);
      ValidationString := 'der String';
      StrLength := Length(ValidationString);
      MazeStream.Write(StrLength, SizeOf(StrLength));
      MazeStream.Write(ValidationString[1], StrLength*SizeOf(ValidationString[1]));
    //...
    finally
      MazeStream.Free
    end;
  end;
end;

procedure TMazeEditor.LoadBinClick(Sender: TObject); //im Spiel ist es fast genau so
var
  MazeStream: TStream;
  StrLength: Integer;
  TmpStr: String;
begin
  if OpenDialog1.Execute then
  begin
    try
      MazeStream := TFileStream.Create(OpenDialog1.FileName, fmOpenRead);
      MazeStream.Read(StrLength, SizeOf(StrLength));
      SetLength(TmpStr,StrLength);
      MazeStream.Read(TmpStr[1], StrLength*SizeOf(TmpStr[1]));
      if TmpStr = 'www.lumpiluk.de.ms - Maze v0.5' then
      begin
        //...
      end;
    finally
      MazeStream.Free;
    end;
  end;
end;
Vielen Dank nochmal!

wicht 5. Apr 2010 13:17

Re: Problem mit TFileStream.Read
 
Delphi-Quellcode:
MazeStream.Write(ValidationString[1], StrLength*SizeOf(ValidationString[1]));
Ich finde folgendes schöner:

Delphi-Quellcode:
MazeStream.Write(String[1], StrLength * SizeOf(Char));
Genau so geht es auch beim lesen. Edit: Das liegt übrigens daran, dass Strings in den neuen Delphi Versionen Unicode-Strings sind, und da ein Zeichen eben 2 Bytes belegt.

sx2008 5. Apr 2010 13:53

Re: Problem mit TFileStream.Read
 
Übrigens:
statt .Read() und .Write() sollte man als Benutzer eines Stream immer .ReadBuffer() und .WriteBuffer() verwenden.
Die Argumente sind gleich.
Allerdings erzeugen die XXXXBuffer() Methoden eine Exception, falls das Lesen oder Schreiben der Daten nicht vollständig sein sollte.

Lumpiluk 6. Apr 2010 10:02

Re: Problem mit TFileStream.Read
 
Zitat:

statt .Read() und .Write() sollte man als Benutzer eines Stream immer .ReadBuffer() und .WriteBuffer() verwenden.
OK, habe ich gemacht.
Was ist denn da genau der Vorteil? Und wofür sind dann das normale Read und Write da?

himitsu 6. Apr 2010 10:06

Re: Problem mit TFileStream.Read
 
Das "normale" Read/Write ließt maximal soviele Byte, wie man angibt, aber der genaue Wert wird im Result zurückgegeben, welches man dann auch auswerten sollte.

ReadBuffer/WriteBuffer versuchen genau soviele Bytes zu lesen, wie man angibt und sie haben kein Result.
Wenn das nicht ging (z.B. Fehler oder einfach nur das Ende der Datei erreicht/überschritten), dann wird eine Exception ausgelöst.

SirThornberry 6. Apr 2010 10:11

Re: Problem mit TFileStream.Read
 
Dann verwende ich doch lieber weiterhin Read und Write und werte das Ergebnis aus anstelle Exceptions anfangen zu müssen.

himitsu 6. Apr 2010 10:17

Re: Problem mit TFileStream.Read
 
Zitat:

Zitat von SirThornberry
Dann verwende ich doch lieber weiterhin Read und Write und werte das Ergebnis aus anstelle Exceptions anfangen zu müssen.

Das ist auch OK so ... jedenfalls wenn du das Ergebnis auch auswertest, was leider viele immer mal wieder vergessen.

Wenn ich den Fehler behandle und das Auslesen dennoch fortsetzen will, dann verwende ich auch .Read
(oder ähnliche Alternativen, da ich immernoch oftmals direkt über die WinAPI geh),
aber wenn der Fehler einfach unbehandelt bleiben soll, bzw. geziehlt zu einem Abbruch führen soll, dann verwende ich quasi das ReadBlock ... wozu selber eine Exception auslösen, wenn es sowas schon gibt. :angel2:


Alle Zeitangaben in WEZ +1. Es ist jetzt 04:55 Uhr.

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