TStream und Strings

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Mathias
Beiträge: 6912
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

TStream und Strings

Beitrag von Mathias »

Ich habe in einem Delphi Tutorial http://www.delphi-treff.de/tutorials/da ... streams/2/
Streams für String angeguckt, dort machen sie eine Umweg über PChar.
Bei mir geht es auch ohne PChar, liegt das am Unterschied zwischen Delphi und FPC ?

Code: Alles auswählen

type
  TDatensatz = record
    ID: integer;
    Name: ansistring;
  end;
 
  TDatenArray = array of TDatensatz;          
 
var
  Daten: TDatenArray;
 
  begin
      Stream.Write(Daten[I].Name, Len);  // ohne PChar
      Stream.Write(PChar(Daten[I].Name)^, Len);  // mit PChar
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Benutzeravatar
theo
Beiträge: 10871
Registriert: Mo 11. Sep 2006, 19:01

Re: TStream und Strings

Beitrag von theo »

Bist du sicher, dass das so die richtigen Werte schreibt? Mit welchen Einstellungen?

Ich denke es müsste - wenn schon - so sein:

Code: Alles auswählen

Stream.Write(Daten[I].Name[1], Len);  

Komoluna
Beiträge: 565
Registriert: So 26. Aug 2012, 09:03
OS, Lazarus, FPC: Windows(10), Linux(Arch)
CPU-Target: 64Bit

Re: TStream und Strings

Beitrag von Komoluna »

@theo: ist nicht der Pointer auf das Array identisch mit dem des ersten Elements?

MFG

Komoluna
Programmer: A device to convert coffee into software.

Rekursion: siehe Rekursion.

Benutzeravatar
theo
Beiträge: 10871
Registriert: Mo 11. Sep 2006, 19:01

Re: TStream und Strings

Beitrag von theo »

Komoluna hat geschrieben:@theo: ist nicht der Pointer auf das Array identisch mit dem des ersten Elements?
Wovon sprichst du?

Komoluna
Beiträge: 565
Registriert: So 26. Aug 2012, 09:03
OS, Lazarus, FPC: Windows(10), Linux(Arch)
CPU-Target: 64Bit

Re: TStream und Strings

Beitrag von Komoluna »

Na, dass MyArray = MyArray[0]... (jedenfalls im Ram)

MFG

Komoluna
Programmer: A device to convert coffee into software.

Rekursion: siehe Rekursion.

marcov
Beiträge: 1102
Registriert: Di 5. Aug 2008, 09:37
OS, Lazarus, FPC: Windows ,Linux,FreeBSD,Dos (L trunk FPC trunk)
CPU-Target: 32/64,PPC(+64), ARM
Wohnort: Eindhoven (Niederlande)

Re: TStream und Strings

Beitrag von marcov »

Komoluna hat geschrieben:Na, dass MyArray = MyArray[0]... (jedenfalls im Ram)
Nein. Das ist der Unterschied zwischen myarray und myarray^ (oder [0])

Pchar wird in Kombinationen mit Strings manchmal als equivalent zu einem String interpretiert (zb x:=x+pc; mit pc:pchar). Aber das ist eine Ausnahme.

Komoluna
Beiträge: 565
Registriert: So 26. Aug 2012, 09:03
OS, Lazarus, FPC: Windows(10), Linux(Arch)
CPU-Target: 64Bit

Re: TStream und Strings

Beitrag von Komoluna »

stimmt, da hab ich was mit c durcheinander gebracht...
da gilt ja auch:

Code: Alles auswählen

arr[7] = 7[arr]
denn bei x[y] werden ja einfach beide Addiert(Pointer und Index * Size)
d.h. wenn index = 0 dann wird der Pointer ja nicht verändert.

MFG

Komoluna
Programmer: A device to convert coffee into software.

Rekursion: siehe Rekursion.

Mathias
Beiträge: 6912
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: TStream und Strings

Beitrag von Mathias »

theo hat geschrieben:Bist du sicher, dass das so die richtigen Werte schreibt? Mit welchen Einstellungen?

Ich denke es müsste - wenn schon - so sein:

Code: Alles auswählen

Stream.Write(Daten[I].Name[1], Len);  
Es kommt mit beiden Varianten das gleiche Ergebniss.

Hier der ganze Code inklusive Einlesung und Ausgabe.

Code: Alles auswählen

type
  TDatensatz = record
    ID: integer;
    Name: ansistring;
  end;
 
  TDatenArray = array of TDatensatz;
 
procedure TForm1.FormCreate(Sender: TObject);
var
  s: string;
begin
  SetLength(s, 20);
  WriteLn(Length(s));
  WriteLn(s + 'doom');
end;
 
procedure ShowDate(daten: TDatensatz);
begin
  WriteLn('ID: ', daten.ID);
  WriteLn('Name: ', daten.Name);
end;
 
const
  filename = 'test.dat';
 
procedure TForm1.SaveClick(Sender: TObject);
var
  Daten: TDatenArray;
  Stream: TFileStream;
  I: integer;
  Len: longint;
begin
  SetLength(Daten, 3);
  Daten[0].ID := 1;
  Daten[0].Name := 'erster Name';
  Daten[1].ID := 2;
  Daten[1].Name := 'zweiter Name';
  Daten[2].ID := 3;
  Daten[2].Name := 'dritter Name';
  Stream := TFileStream.Create(filename, fmCreate);
  try
    Len := Length(Daten);
    Stream.Write(Len, SizeOf(Len));
    for I := 0 to Length(Daten) - 1 do begin
      Stream.Write(Daten[I].ID, SizeOf(Daten[I].ID));
      Len := Length(Daten[I].Name);
      Stream.Write(Len, SizeOf(Len));
      Stream.Write(Daten[I].Name, Len);
      //      Stream.Write(PChar(Daten[I].Name)^, Len);
    end;
  finally
    Stream.Free;
  end;
end;
 
procedure TForm1.LoadClick(Sender: TObject);
var
  Daten: TDatenArray;
  Stream: TFileStream;
  I: integer;
  Len: longint;
begin
  Stream := TFileStream.Create(filename, fmOpenRead);
  try
    Stream.Read(Len, SizeOf(Len));
    SetLength(Daten, Len);
 
    for I := 0 to Len - 1 do begin
      Stream.Read(Daten[I].ID, SizeOf(Daten[I].ID));
      Stream.Read(Len, SizeOf(Len));
      SetLength(Daten[I].Name, Len);
      //      Stream.Read(PChar(Daten[I].Name)^, Len);
      Stream.Read(Daten[I].Name, Len);
    end;
  finally
    Stream.Free;
  end;
  for i := 0 to Length(Daten) - 1 do begin
    ShowDate(Daten[i]);
  end;
end;  
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

wp_xyz
Beiträge: 5142
Registriert: Fr 8. Apr 2011, 09:01

Re: TStream und Strings

Beitrag von wp_xyz »

Dann überprüfe mal mit einem Editor, ob in der Datei wirklich das steht, was du erwartest...

Wenn du per "Stream.Write(Daten.Name, Len)" in den Stream schreibst, beginnst du nicht beim 1. Zeichen, sondern bei der Verwaltungsinformation (Referenzzähler, Stringlänge), die vor dem 1. Zeichen steht.

Mathias
Beiträge: 6912
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: TStream und Strings

Beitrag von Mathias »

Wen ich anstelle

Code: Alles auswählen

Daten[I].Name
Daten[I].Name[1]
nehme, komm nur Müll raus.
Auch eine Mischung zwischen PChar und Daten.Name geht nicht.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

wp_xyz
Beiträge: 5142
Registriert: Fr 8. Apr 2011, 09:01

Re: TStream und Strings

Beitrag von wp_xyz »

Was heißt "geht nicht"? Programm meldet Fehler? Oder: die Leseroutine ergibt andere Daten als geschrieben wurden? Oder: wenn die Datei mit einem Editor geöffnet wird, stehen die geschriebenen Daten nicht drin?

Probier folgendes und öffne die Test-Datei mit einem Editor. In dem von dir favorisierten Fall steht in der Datei nur Blödsinn

Code: Alles auswählen

program Project1;
 
{$mode objfpc}{$H+}
 
uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes
  { you can add units after this };
 
var
  stream: TFileStream;
  s: ansistring;
  Len: Integer;
begin
  s := 'Hallo Welt';
  Len := Length(s);
  stream := TFileStream.Create('test.txt', fmCreate);
  //stream.Write(s[1], Len);  // --> ok
  //stream.Write(PChar(s)^, Len);  // --> ok
  stream.Write(s, Len);  // --> Unsinn
  stream.Free;
end.  

Mathias
Beiträge: 6912
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: TStream und Strings

Beitrag von Mathias »

Folgender Test bringt bei s2 "Hello Welt";
Öffne ich die Datei mit dem TextEditor, so kommen unleserliche Zeichen, so wie du schreibst.
Bei deinem Beispiel ist die Text-Datei leserlich.
Die Datei ist auch 10Byte lang, so wie in deinem Beispiel.

Aber wieso geht mein erstes Beispiel mit der Array mit [1] nicht ?

Code: Alles auswählen

program Project1;
 
{$mode objfpc}{$H+}
 
uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes
  { you can add units after this };
 
var
  stream: TFileStream;
  s1, s2: ansistring;
  Len: Integer;
begin
  s1 := 'Hallo Welt';
  s2 := '----------';
  Len := Length(s1);
  stream := TFileStream.Create('v:\test.txt', fmCreate);
//  stream.Write(s1[1], Len);  // --> ok
  //stream.Write(PChar(s1)^, Len);  // --> ok
  stream.Write(s1, Len);  // --> Unsinn
  stream.Free;
 
  stream := TFileStream.Create('v:\test.txt', fmOpenRead);
//  stream.Read(s2[1], Len);  // --> ok
  //stream.Read(PChar(s2)^, Len);  // --> ok
  stream.read(s2, Len);  // --> Unsinn
  stream.Free;
 
  WriteLn(s2);
 
  ReadLn;
end.    
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Warf
Beiträge: 2118
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: TStream und Strings

Beitrag von Warf »

Folgender Test bringt bei s2 "Hello Welt";
Öffne ich die Datei mit dem TextEditor, so kommen unleserliche Zeichen, so wie du schreibst.
Bei deinem Beispiel ist die Text-Datei leserlich.
Die Datei ist auch 10Byte lang, so wie in deinem Beispiel.
Das liegt daran dass du den Zeiger in die Datei Schreibst und den auch wieder ausließt, wärend deines Durchlaufes auch kein Problem, da dieser Zeiger in der Selben routine noch aktiv ist. Das Hauptproblem ist dass du mit Stream.Read(s2, Len); 10 Byte in den Stack ließt, wobei nur 4 byte in den Pointer gehen, mit den restlichen 6 byte überschreibst du den Stack.
Das ist da du in der selben routine bist und der Stack sich zwischen write und read nicht verändert hat kein problem, aber nutze immer str[1], da du sonst deinen Stack ließt und beschreibst, aber AnsiStrings nicht im Stack liegen, sondern nur der Zeiger im stack liegt

wp_xyz
Beiträge: 5142
Registriert: Fr 8. Apr 2011, 09:01

Re: TStream und Strings

Beitrag von wp_xyz »

@Warf: schön beschrieben

@Mathias: Kommentiere mal den Schreib-Teil deines Programms aus und lasse das Programm nochmals laufen, so dass die Datei nur gelesen wird. Nun wird für s2 etwas anderes angezeigt. Das beweist, dass der String als Zeichenkette nicht in den Stream geschrieben wurde.

Mathias
Beiträge: 6912
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: TStream und Strings

Beitrag von Mathias »

Verstehe ich es jetzt richtig ?

Bei folgen Code Schreibt er bei Writeln(s2) "Hallo Welt".
s2 bekommt den gleichen Speicherbereich zugeordnet wie s1, da dieser in test.txt steht.

Code: Alles auswählen

var
  stream: TFileStream;
  s1, s2: ansistring;
  Len: Integer;
begin
  s1 := 'Hallo Welt';
  s2 := '----------';
  Len := Length(s2);
 
  stream := TFileStream.Create('v:\test.txt', fmOpenRead);
  stream.read(s2, Len);  // --> Unsinn
  stream.Free;
 
  WriteLn(s2);
 
  ReadLn;
end.    
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Antworten