Entwickler-Ecke

Dateizugriff - Verständnisfrage zu TStream.Seek


IhopeonlyReader - Mo 02.09.13 13:05

Moderiert von user profile iconNarses: Abgetrennt von [url=http://www.entwickler-ecke.de/viewtopic.php?t=111829]hier[/url].

ich habe noch einen Unterschied festgestellt !
Falls man etwas in einem Stream überschreiben will, MUSS man Writebuffer verwenden (vorher Position entsprechend setzten)
Write hängt es hinten dran oder macht gar nix damit :O
Writebuffer überschreibt und fertig :)


Hab ich heute erst bemerkt, denn

Delphi-Quelltext
1:
2:
3:
4:
  FS.Read( aByte, 1 );      // Read
  aByte := aByte xor Mask;  // Crypt
  FS.Position := FS.Position -1// go back
  FS.WriteBuffer( aByte, 1 ); //Write

das klappt so, dagegen klappt

Delphi-Quelltext
1:
2:
3:
4:
  FS.Read( aByte, 1 );      // Read
  aByte := aByte xor Mask;  // Crypt
  FS.Position := FS.Position -1// go back
  FS.Write( aByte, 1 ); //Write

nicht !


oder gibt es dafür andere erklärungen`?


Blup - Mo 02.09.13 17:55

Wer "Write" verwendet muss immer den Rückgabewert auswerten (sonst kann man nur raten wie das Ergebnis zustande kommt).
Abhängig vom Stream eventuell auch noch LastError.
Das sollte man besser dem Stream selbst überlassen, sonst schafft man unnötige Abhängigkeiten.


rushifell - Di 03.09.13 00:39

Bist du Dir sicher, dass das Offset zum Lesen und Schreiben korrekt ist?

Kennst Du die Methode 'seek'?


IhopeonlyReader - Mi 04.09.13 10:39

ob seek( x ) oder Position := x; ich merke keinen unterschied !


jaenicke - Mi 04.09.13 13:30

Das könnte daran liegen, dass Position intern auch nur Seek verwendet. ;-)


rushifell - Mi 04.09.13 17:05

Warum sollte das im Ergebnis einen Unterschied machen? Habe ich nie behauptet.

Habe mich nur gewundert, dass Du die Position so umständlich setzt.


Martok - Mi 04.09.13 18:02

Hach ist hier viel Halbwissen. Use the source, Luke!

Delphi-Quelltext
1:
2:
3:
4:
5:
procedure TStream.WriteBuffer(const Buffer; Count: Longint);
begin
  if (Count <> 0and (Write(Buffer, Count) <> Count) then
    raise EWriteError.CreateRes(@SWriteError);
end;

:arrow: einziger Unterschied ist eine Exception wenn's nicht klappt.

user profile iconIhopeonlyReader hat folgendes geschrieben Zum zitierten Posting springen:
ob seek( x ) oder Position := x; ich merke keinen unterschied !
Doch, da ist einer. Ein Lesezugriff auf Position ist ein Seek(0, soFromCurrent), ein Schreibzugriff Seek(aValue, soFromBeginning). Verwendest du das Property, sind da zwei Dateioperationen, Seek(-1, soFromCurrent) direkt aufzurufen ist also schneller. Wenn man das ein paar tausend mal macht, fällt das schon (natürlich auch abhängig von der Streamklasse) auf.


IhopeonlyReader - Mi 04.09.13 18:16

user profile iconMartok hat folgendes geschrieben Zum zitierten Posting springen:
Hach ist hier viel Halbwissen. Use the source, Luke!

Delphi-Quelltext
1:
2:
3:
4:
5:
procedure TStream.WriteBuffer(const Buffer; Count: Longint);
begin
  if (Count <> 0and (Write(Buffer, Count) <> Count) then
    raise EWriteError.CreateRes(@SWriteError);
end;


wenn das so wäre, was ich gerne glaube, würde ich gerne eine Erklärung haben, warum es mit write nicht funktioniert, mit writebuffer schon !


rushifell - Mi 04.09.13 19:22

Deshalb habe ich geschrieben, das es im "Ergebnis" keinen Unterschied macht. Beides funktioniert. Dass Position intern Seek aufruft, hat ja bereits Jaenicke geschrieben. Dass es daher mehr Sinn macht, direkt Seek zu verwenden ist dadurch ja klar.

In den Personal Editions gibt's leider keinen Source.


IhopeonlyReader - Mi 04.09.13 20:24

ich meinte Write und Writebuffer.. Writebuffer funktioniert, write alleine nicht


jaenicke - Mi 04.09.13 20:52

Ohne ein konkret funktionierendes Beispielprojekt wird da wohl niemand mehr dazu sagen können. Bei mir haben Write und WriteBuffer immer funktioniert wie sie sollen. :nixweiss:


IhopeonlyReader - Mi 04.09.13 21:22


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
var aByte,
    Mask: Byte;
    C: Integer;
    FS: TFileStream;
begin
FS := TFileStream.Create( aFileName, fmOpenReadWrite or fmShareDenyRead or fmShareDenyWrite );
try
FS.Position := 0;
For C := 1 to FS.Size do
  begin
{Mask erstellen}
  FS.Read( aByte, 1 );      // Read
  aByte := aByte xor Mask;  // Crypt
  Seek( FS.Position -1 , soFromBeginning ); // go back
  FS.WriteBuffer( aByte, 1 ); //Write
  end;
finally
  FS.Free;
  end;
end;


aber als ich jetzt Write verwendete funktionierte es, mein Delphi spinnt öfter mal :(
Delphi neustarten lässt write plötzlich auch in der compilierten exe funktionieren :)

Und bei einem anderen Programm habe ich sogar das Problem, dass sich meine Form nicht enabled obwohl ich sie nie disable :O Meine Form wird also nicht ansprechbar, bei allen anderen Projekten funktionierts, nur da nicht... es liegt ganz sicher kein fehler vor ! Aber ich muss wohl wieder 2-3 tage warten und dann Wunder funktionierts wieder..


jaenicke - Mi 04.09.13 22:11

:shock:
Vor dem Seek hast du das FS vergessen. Dass das nicht geht, ist klar...

Wie wärs einfach so...

Delphi-Quelltext
1:
FS.Seek(-1, soFromCurrent);                    


rushifell - Mi 04.09.13 22:20

Zeile 8 ist überflüssig, einfach weglassen. Und wenn du FS.Read verwendest, musst du den Rückgabewert überprüfen.

Eine For-Schleife zu verwenden, wäre mir auch nicht geheuer.


IhopeonlyReader - Mi 04.09.13 22:46

ja, eigentlich steht da
FS.Position := FS.Position -1;
aber ich wollte es auf "eure" Vorschläge schonmal anpassen..

Ob Zeile 8 überflüssig ist, weiß ich nicht. Lieber initialisieren ;)

und warum soll eine For-Schleife nicht geheuer sein? Ich gehe Byte für Byte durch, dass ich das genau so oft mache, wie viele Bytes enthalten sind ist doch logisch oder?

Bei Read und Write überprüfe ich keine Rückgabewerte ! Es wird das gemacht, was möglich ist.
Wenn ich ein 2tes mal mache, wird es ebenso nicht möglich sein, und somit macht es kein Unterschied ! ggf. könnte ich, falls der positionszeiger nicht aktualisiert wird, da das schreiben/lesen nicht geklappt hat, diesen einen weiter setzten, sonst versucht er immer an der "nicht möglich" stelle zu lesen, aber wenn ich gehe davon aus, dass alles oder nichts klappt. ich kenne keine Datei die nur zulässt Byte 1-X und ab X+1 zu beschreiben..


rushifell - Mi 04.09.13 23:11

Du bist sehr beratungsresistent. ;-)


Gerd Kayser - Mi 04.09.13 23:27

user profile iconIhopeonlyReader hat folgendes geschrieben Zum zitierten Posting springen:
Falls man etwas in einem Stream überschreiben will, MUSS man Writebuffer verwenden (vorher Position entsprechend setzten)

Das ist falsch, denn das hier funktioniert:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
procedure TMainform.Button1Click(Sender: TObject);
var
  FS       : TFileStream;
  Schleife : Integer;
  Wert     : Byte;
  Maske    : Byte;
begin
  FS := TFileStream.Create('f:\x.xxx', fmCreate);
  try
    Wert := 65;
    for schleife := 1 to 100 do
      FS.Write(Wert, SizeOf(Wert));
    FS.Position := FS.Position - 1;
    FS.Read(Wert, 1);
    Maske := 122;
    Wert := Wert XOR Maske;
    FS.Position := FS.Position - 1;
    FS.Write(Wert, SizeOf(Wert));
  finally
    FS.Free;
  end;
end;


IhopeonlyReader - Do 05.09.13 13:41

user profile iconGerd Kayser hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconIhopeonlyReader hat folgendes geschrieben Zum zitierten Posting springen:
Falls man etwas in einem Stream überschreiben will, MUSS man Writebuffer verwenden (vorher Position entsprechend setzten)

Das ist falsch, denn das hier funktioniert:


aber als ich jetzt Write verwendete funktionierte es, mein Delphi spinnt öfter mal :( (sagte ich schonmal)



user profile iconrushifell hat folgendes geschrieben Zum zitierten Posting springen:
Du bist sehr beratungsresistent. ;-)

Naja, deine Aussage
Zitat:
Eine For-Schleife zu verwenden, wäre mir auch nicht geheuer.

sagt soviel wie, "ich würde eine andere schleife verwenden"
da ich sonst nur while und repeat kenne und diese langsamer sind frage ich mich, warum ich while oder repeat benutzen sollte, ich weiß doch wieviele Bytes ich durchgehen muss...

Zu dem "FS vergessen" habe ich auch ausgesagt, dass das eine Anpassung war und nicht direkt die Fehlerquelle..


jaenicke - Do 05.09.13 13:46

user profile iconIhopeonlyReader hat folgendes geschrieben Zum zitierten Posting springen:
aber als ich jetzt Write verwendete funktionierte es, mein Delphi spinnt öfter mal :( (sagte ich schonmal)
Delphi spinnt da sicher nicht mal und mal nicht. Solange du nicht gerade Speicherprobleme hast, sprich an Stellen geschrieben hast, die nicht zum Schreiben waren, kann das nicht sein.

Wenn du debuggst und dabei im Debugger auf die Werte schauen willst, solltest du die Optimierung in den Projektoptionen deaktivieren. Das Verhalten mit Optimierung kann im Debugger schon mal komisch aussehen. ;-)


rushifell - Do 05.09.13 17:42

Wo hast Du das aufgeschnappt, dass man nach dem Erzeugen eines Streams die Position auf 0 bzw. den Anfang setzen muss? Hab ich noch nie gesehen, lass mich aber gerne eines Besseren belehren.

Wenn Du die Rückgabewerte nicht prüfst, ist das zumindest meiner Meinung nach schlechter Programmierstil. Dann nimm lieber ReadBuffer.

Nicht alles, was funktioniert ist gut. Die For-Schleife ist schon in Ordnung. Warum soll aber die For-Schleife schneller sein? Hast Du Beweise dafür?


IhopeonlyReader - Do 05.09.13 17:57

user profile iconrushifell hat folgendes geschrieben Zum zitierten Posting springen:
Wo hast Du das aufgeschnappt, dass man nach dem Erzeugen eines Streams die Position auf 0 bzw. den Anfang setzen muss? Hab ich noch nie gesehen, lass mich aber gerne eines Besseren belehren.

ich bin selbstbeibringer und ich habe mich "gezwungen" alles was ich irgendwo erstelle zu initialisieren. Das führt dazu, dass ich z.B. bei einer verketteten liste auch den Pointer vom next auf Nil setzte

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
//Beispiel
NextPart := TNextPart.create;
NextPart.Content := NewContent;
NextPart.Next := Nil//hier aus reiner "Sicherheit"
Tail.Next := NextPart;
Tail := Tail.next;



user profile iconrushifell hat folgendes geschrieben Zum zitierten Posting springen:
Wenn Du die Rückgabewerte nicht prüfst, ist das zumindest meiner Meinung nach schlechter Programmierstil. Dann nimm lieber ReadBuffer.

Ich habe nun, falls der Rückgabewert ungleich 1 ist dieses "übersprungen".. Fehlermeldungen gib ich ungern aus, da dann meist nicht 1 sondern gefühlte 1000 kommen...
da das Programm nur für mich ist, wird es auch keinen geben, der von fehlenden Fehlermeldungen genervt sein will...

user profile iconrushifell hat folgendes geschrieben Zum zitierten Posting springen:
Nicht alles, was funktioniert ist gut. Die For-Schleife ist schon in Ordnung. Warum soll aber die For-Schleife schneller sein? Hast Du Beweise dafür?


user profile iconMeierZwoo hat folgendes geschrieben:
WHILE und REPEAT/UNTIL nehmen sich nichts, sind aber (grade beim schnellen Tippseln für einen Zeittest) kritisch, weil die Abbruchbedingung unbedingt erreicht werden muß (Die man ja auch meist nicht hat und nachher nicht weiss, wie oft durchlaufen - es sei denn, man schreibt dies noch zusätzich mit).

Eine FOR Schleife wäre schneller, weil dort nur ein Wert erhöht werden und keine Abbruchbedingung ermittelt werden muß. Außerdem hat man bei FOR gleich den Teiler für die Zeitmessung.
aus dem Thread : http://www.entwickler-ecke.de/viewtopic.php?t=111508


rushifell - Do 05.09.13 18:39

Schau Dir doch einfach mal die Online-Hilfe von Delphi an, dort gibt es in der Regel auch Beispiele. Ich müsste wirklich sehr täuschen, wenn es notwendig wäre, die Position auf 0 zu setzen.

Das mit der For-Schleife ist nur eine Behauptung von einer Person, woher weisst Du, dass diese Behauptung stimmt? Im Idealfall vielleicht schon. Achtung, sonst werden wir zu Off-Topic.

Bei einem Fehler in der For-Schleife kannste die Schleife mit Break abbrechen/beenden.


jaenicke - Do 05.09.13 22:50

user profile iconrushifell hat folgendes geschrieben Zum zitierten Posting springen:
Das mit der For-Schleife ist nur eine Behauptung von einer Person, woher weisst Du, dass diese Behauptung stimmt? Im Idealfall vielleicht schon.
Das ist auch so, allerdings ist der Unterschied bei wenigen Durchläufen nicht spürbar. Im Anhang ein Vergleich, die in der Schleife wiederholten Zeilen sind rot markiert.
Man sieht, dass bei der for-Schleife die Initialisierung vorher passiert, so dass dort 4 Assemblerbefehle mehr gebraucht werden als bei der while-Schleife. Dafür werden bei der while-Schleife 2 Assemblerbefehle mehr bei jedem Durchlauf benötigt.
Heißt: while ist ab drei Durchläufen langsamer. ;-)


rushifell - Fr 06.09.13 02:18

Danke jaenicke, so wie Du's beschrieben hast, ist es auch nachvollziehbar. Und das trifft auch zu, wenn in der For-Schleife eine Abbruchbedingung vorhanden ist?


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
FOR i:=0 to FS.Count-1 Do
  Begin
   ...
   Numread:=FS.Read(b,1);
   IF Numread<>1 then Break;
  end;


Kann mir Dein Beispiel leider erst am Montag anschauen. Bin z.Z. leider nur mobil im Netz.


jaenicke - Fr 06.09.13 08:27

user profile iconrushifell hat folgendes geschrieben Zum zitierten Posting springen:
Danke jaenicke, so wie Du's beschrieben hast, ist es auch nachvollziehbar. Und das trifft auch zu, wenn in der For-Schleife eine Abbruchbedingung vorhanden ist?
Das ist etwas anderes. Die for-Schleife ist unter der Voraussetzung schneller, dass man vorher weiß wie viele Durchläufe es gibt. Wenn man hingegen eine Abbruchbedingung prüft, sind das natürlich zusätzliche Befehle. Wenn man diese Prüfung auch als Schleifenbedingung benutzen könnte, macht while/repeat mehr Sinn, da diese Prüfung ja nicht bei for wegfällt und somit die Prüfungen der for-Schleife zusätzlich sind.

Ist hingegen die normale Anzahl der Durchläufe vorgegeben und das andere nur eine zusätzliche Bedingung ist weiter eher for schneller, wobei man da im Einzelfall schauen müsste wie der Compiler das optimiert. In der Regel sollte das aber nur die Prüfung und ein Sprung sein, und das wäre dann in der while-Schleifenbedingung genauso, so dass die gesparten Befehle bei for weiter ein Vorteil sind.

Nebenbei:
Wenn man die Anzahl der Durchläufe kennt, ist for auch übersichtlicher, da man dies bei fremdem Quelltext sofort erkennt. Bei einer while- oder repeat-Schleife muss man erst in der Schleife schauen wann diese abbricht usw., so dass man mehr Zeit braucht um den Quelltext zu überblicken.