Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi StreamWrite erzeugt nur 0 Byte-File (https://www.delphipraxis.net/115063-streamwrite-erzeugt-nur-0-byte-file.html)

little_budda 5. Jun 2008 10:13


StreamWrite erzeugt nur 0 Byte-File
 
Hallo,

ich nutze folgende Code um eine binäre Datei zu erzeugen.
Delphi-Quellcode:
procedure Tfrm_main.Button1Click(Sender: TObject);
var aFileBuffer : array of byte;
    iFileLength, ix, iByteCounter : integer;
    datei : tfilestream;
    bStreamOk : boolean;
    s_filename, startpfad : string;
begin
  iFileLength := strtoint(edit1.text); // Aktueller Wert: '20980826'
  // Array erzeugen
  setLength(aFileBuffer, iFileLength);

  for ix := 0 to (iFileLength - 1)
    do aFileBuffer[ix] := 55;

  // Startpfad ermitteln
  getdir(0, startpfad);                            // Startpfad einlesen
  if (startpfad[length(startpfad)] <> '\')
    then startpfad := startpfad + '\'; // Pfad ggf. korrigieren

  // Dateiname zuweisen
  s_filename := startpfad + edit2.Text; // Aktueller Wert: 'data.bin'

  bStreamOk := true;
  try
    datei := Tfilestream.create(s_filename, fmcreate or fmShareExclusive);
  except
    bStreamOk := false;
  end;

  if (bStreamOk = true) then
    begin
      // Schreiben der DateiInhalte in den Stream
      try
        iByteCounter := datei.Write(aFileBuffer, iFileLength);
      finally
        datei.free;
      end;
      // Prüfen ob die korrekte Anzahl Bytes geschrieben wurde
      if (iByteCounter <> iFileLength)
        then showmessage('Stream wurde erzeugt, hat aber die falsche Größe.')
        else showmessage('Stream wurde erzeugt.');
    end
  else
    begin
      showmessage('Stream konnte nicht erzeugt werden.');
    end;
end;
Leider wird die Datei aber immer nur nit einer Länge von 0 Byte angelegt.
Was bitte mache ich denn falsch?

Gruß Holger.

:gruebel:

mkinzler 5. Jun 2008 10:17

Re: StreamWrite erzeugt nur 0 Byte-File
 
Delphi-Quellcode:
iByteCounter := datei.WriteBuffer(aFileBuffer, iFileLength);
Statt
Delphi-Quellcode:
if (bStreamOk = true) then
besser
Delphi-Quellcode:
if bStreamOk then

little_budda 5. Jun 2008 10:21

Re: StreamWrite erzeugt nur 0 Byte-File
 
Danke

p80286 5. Jun 2008 10:30

Re: StreamWrite erzeugt nur 0 Byte-File
 
Hallo little Budda,

Ich habe Deinen Code einmal ausprobiert und leicht geändert:
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var aFileBuffer : array of byte;
    iFileLength, ix, iByteCounter : integer;
    datei : tfilestream;
    bStreamOk : boolean;
    s_filename, startpfad : string;
begin
  [b]iFileLength := 123; // Aktueller Wert: '20980826'[/b]
  // Array erzeugen
  setLength(aFileBuffer, iFileLength);

  for ix := 0 to (iFileLength - 1)
    do aFileBuffer[ix] := 55;

  // Startpfad ermitteln
  getdir(0, startpfad);                            // Startpfad einlesen
  if (startpfad[length(startpfad)] <> '\')
    then startpfad := startpfad + '\'; // Pfad ggf. korrigieren

  // Dateiname zuweisen
 [b] s_filename := 'c:\temp\test.bin'; // Aktueller Wert: 'data.bin'[/b]
  bStreamOk := true;
  try
    datei := Tfilestream.create(s_filename, fmcreate or fmShareExclusive);
  except
    bStreamOk := false;
  end;

  if (bStreamOk = true) then
    begin
      // Schreiben der DateiInhalte in den Stream
      try
        iByteCounter := datei.Write(aFileBuffer, iFileLength);
      finally
        datei.free;
      end;
      // Prüfen ob die korrekte Anzahl Bytes geschrieben wurde
      if (iByteCounter <> iFileLength)
        then showmessage('Stream wurde erzeugt, hat aber die falsche Größe.')
        else showmessage('Stream wurde erzeugt.');
    end
  else
    begin
      showmessage('Stream konnte nicht erzeugt werden.');
    end;
end;
und siehe da es wird eine 123Byte große Datei erzeugt.
Schau Dir im Debugger die Übergabe der Dateigröße einmal an.

Gruß
K-H

Luckie 5. Jun 2008 10:42

Re: StreamWrite erzeugt nur 0 Byte-File
 
Also irgendwie sieht euer Code komisch aus.

Dieses bStreamOK stört mich zum Beispiel. Das kann man doch so lösen:;
Delphi-Quellcode:
datei := TFileStream.Create(...);
if Assigned(datei) then
begin
  try
    try
      BytesWritten := datei.Write(...);

    except
      raise Exception....
  finally
    datei.Free;
  end;
end;
Desweiteren würde ich die Daten/Code von der Oberfläche trennen, also den Code in eine separate Methode auslagern, die von der Schaltfläche aufgerufen wird:
Delphi-Quellcode:
procedure Tfrm_main.Button1Click(Sender: TObject);
begin
  try
    SchreibeDatei(...);
  except
    on E: Exception do ShowMessage(E.Message);
  end;

Muetze1 5. Jun 2008 10:47

Re: StreamWrite erzeugt nur 0 Byte-File
 
GetDir(0, x); ist eine schlechte Wahl, da es der aktuelle Pfad ist und dieser somit nicht zwingend der "Startpfad" ist. Ein Open/SaveDialog z.B. ändern diesen Standardpfad standardmässig mit den Nutzeraktionen. Hier solltest du lieber auf ParamStr(0) zurück greifen.

Auch ist dein Test auf ein abschließenden Backslash eine Fehlerquelle, wenn der string mal leer ist. Dann würde es zu einer Zugriffsverletzung bzw. einem Range Check Error kommen. Nutze lieber dazu die von der VCL definierten Funktion IncludeTrailingPathDelimiter().

Deine Prüfung ob die Erstellung der TFileStream Instanz erfolgreich war, könnte man noch in zwei Dingen ändern/verbessern: Zum einen würde eine Exception aus dem Constructor die Instanzenvariable niemals zuweisen, da er durch die Exception diesen Coder erst gar nicht mehr ausführt. Von daher kannst du auch einfach die Instanzenvariable auf NIL setzen vor dem try und dann danach abprüfen, ob diese ungleich nil ist.
Und zum anderen kann der Constructor nur durch eine Exception abgebrochen werden und somit kannst du auch einfach den Except Block hinter deine Dateiroutinen verschieben, weil durch die Exception würde er diese nicht mehr ausführen und am Ende gefangen werden.

/EDIT: Toll, alle schreiben während ich hier tippe.

@Luckie: Du musst explizit datei auf nil setzen, weil es eine (nicht initialisierte) lokale Variable ist. Und wenn, ein Constructor gibt niemals nil zurück, sondern wird höchstens mit einer Exception abgebrochen und damit würde deine Abfrage erst gar nicht mehr zum Zuge kommen.
Und warum mit Except eine Exception fangen um dann eine eigene zu werfen? Warum willst du die zusätzlichen Informationen der originalen Exception wegwerfen? Warum eine unspezifizierte eigene Exception? Wenn, dann wieder re-raisen mit raise, aber dann brauchste auch kein Except Block, den kannst du dir dann auch gleich sparen.
Und es fehlt ein end; bei deinem Code...

@p80286: Wie schon erwähnt, würde ich den Vergleich auf true unbedingt abändern!

Luckie 5. Jun 2008 10:50

Re: StreamWrite erzeugt nur 0 Byte-File
 
Zitat:

Zitat von Muetze1
@Luckie: Du musst explizit datei auf nil setzen, weil es eine (nicht initialisierte) lokale Variable ist. Und wenn, ein Constructor gibt niemals nil zurück, sondern wird höchstens mit einer Exception abgebrochen und damit würde deine Abfrage erst gar nicht mehr zum Zuge kommen.
Und warum mit Except eine Exception fangen um dann eine eigene zu werfen? Warum willst du die zusätzlichen Informationen der originalen Exception wegwerfen? Warum eine unspezifizierte eigene Exception? Wenn, dann wieder re-raisen mit raise, aber dann brauchste auch kein Except Block, den kannst du dir dann auch gleich sparen.
Und es fehlt ein end; bei deinem Code...

Das war nur so mal eben schnell dahin getippt. Es ist natürlich richtig, dass man einfach ein raise macht und keine neue Exception wirft. Wie würdest du es lösen?

p80286 5. Jun 2008 11:02

Re: StreamWrite erzeugt nur 0 Byte-File
 
Hallo Little Budda,

sorry ich hab mich etwas vergallopiert, Aus mir unerfindlichen Gründen ist bei der Dateigröße von $3AEC (ca 15 000) Schluß mit lustig. Da liegt das Problem weniger in Deinem Code als irgendwo anders.

Gruß
K-H

p80286 5. Jun 2008 11:05

Re: StreamWrite erzeugt nur 0 Byte-File
 
@Muetze

Naja halte ich für Geschmackssache. Deine Version gefällt mir besser aber ist sie darum richtiger. Ist schließlich little Buddas Code und er muß sich darin zurechtfinden.

Gruß
K-H

Muetze1 5. Jun 2008 11:16

Re: StreamWrite erzeugt nur 0 Byte-File
 
Zitat:

Zitat von Luckie
Das war nur so mal eben schnell dahin getippt. Es ist natürlich richtig, dass man einfach ein raise macht und keine neue Exception wirft. Wie würdest du es lösen?

Bezugnehmend auf dein Beispiel wo du es direkt darauf anlegst die Exceptions ausserhalb des Aufrufes zu fangen: gar keinen Except Block. Nur ein finally Block als Resourcenschutzblock für die Instanz, eventuelle Exceptions gehen nach draussen und werden gefangen und die Instanz wird definitiv freigegeben.

Im gesamten dann:

Delphi-Quellcode:
procedure Tfrm_main.Button1Click(Sender: TObject);
begin
  try
    if DateiSchreiben(ExtractFilePath(ParamStr(0)) + Edit2.Text, StrToIntDef(Edit1.Text, 0)) then
      MessageDlg('Datei wurde erfolgreich gespeichert.', mtInformation, [mbOk], 0);
  except
    on e:exception do
      MessageDlg(format('Es ist beim Schreiben der Datei ein Fehler aufgetreten.'#10#13#10#13'(%s) %s', [E.Classname, E.Message]),
                 mtError, [mbOk], 0);
  end;
end;

function DateiSchreiben(const AFilename: string; const AFileSize: Int64);
const
  coBlockSize = 16384;
var
  lStream: TStream;
  lFileData: string;
  lBytesToWrite: Int64;
  lWritten: Integer;
begin
  result := false;

    // Erstmal den Stream öffnen. Wenn das nicht klappt, brauch ich kein Array...
  lStream := TFileStream.Create(AFilename, fmCreate or fmShareDenyWrite);
  try
    lFileData := StringOfChar(coBlockSize, #55);

    lBytesToWrite := AFileSize;
    while ( lBytesToWrite > 0 ) do
    begin
      lWritten := lStream.WriteBuffer(lFileData[1], Min(lBytesToWrite, coBlockSize));

      if lWritten <> Min(lBytesToWrite, coBlockSize) then
        exit;

      Dec(lBytesToWrite, lWritten);
    end;

    result := true;
  finally
    lStream.free;
  end;
end;
Warum so?

1. Ein dynamisches Array ohne Begrenzung ist schlecht, weil bei einer 2 GB Datei möchte ich mal sehen, was zu der Speichermanager zu der Alloziierung sagt.
2. Da die Daten immer gleich waren, hat es sich hier angeboten StringOfChar() zu nutzen.
3. Exceptions, egal wo, werden geworfen und kommen so richtig beim Aufrufer an.
4. Der Rückgabewert gibt den eigentlichen Erfolg zurück. Man kann alternativ mit der Verlagerung des Exception Handlers in die Funktion diese zu einer reinen Funktion machen, die alles über das result regelt.
5. Oberfläche und Implementation sind getrennt. Alles wichtige was die Funktion braucht, wird ihr übergeben und ausserhalb entsprechend aufbereitet.

Und allgemeiner Hinweis: Wenn es nur darum geht eine Platzverschwendung mit bestimmter Größe zu erzeugen, dann einfach TFileStream anlegen und die Size Property setzen. Der Inhalt der Datei ist dann aber Zufall.

Zitat:

Zitat von p80286
Naja halte ich für Geschmackssache.

Und genau dies ist es eben nicht. Es ist keine reine Geschmackssache sondern eine offene Fehlerstelle. False ist mit 0 definiert und kann direkt verglichen werden. True ist alles was nicht false ist, somit alles ungleich 0. Die Konstante True ist aber nicht mit der Menge der ganzen Zahlen exklusiv der 0 definiert, sondern mit einem einzigen Ordinalwert (1 oder -1? Weiss es gerade nicht genau) und darin liegt das Problem. Wenn du direkt vergleichst, sagst du dem Compiler: Wenn das direkt und nur dieser Wert ist, dann mache. Wenn du den direkten Vergleich weglässt, dann evaluiert die IF Anweisung selbst und das setzt der Compiler auf ein ungleich 0 Vergleich um. Und das ist nicht Geschmackssache sondern ein riesiger Unterschied!

tomsel 5. Jun 2008 11:41

Re: StreamWrite erzeugt nur 0 Byte-File
 
Delphi-Quellcode:
...
  bStreamOk := true;
  try
    datei := Tfilestream.create(s_filename, fmcreate or fmShareExclusive);
  except
    bStreamOk := false;
  end;

  if (bStreamOk = true) then
    begin

Bevor ihr euch weiter in eine Grundsatzdiskussion verheddert, sagt mir doch mal bitte einer, wo in desem konkreten Fall das Problem mit dem Vergleich mit true genau liegt.

[ED]
bStreamOK hat beim Vergleich entweder den Wert der Konstanten TRUE oder den von FALSE, und keinen Anderen. Als Fehlerursache im Zusammenhang mit der Ausgangsfrage kommt der true-Vergleich daher m.E. nicht in Betracht. Oder irre ich mich, das wollte ich eigentlich nur wissen :mrgreen:
[/ED]

Muetze1 5. Jun 2008 11:48

Re: StreamWrite erzeugt nur 0 Byte-File
 
Vorschlag: lies bitte den letzten Teil meines Beitrags zuvor, ab dem letzten Quote durch.

Zitat:

Zitat von tomsel
bStreamOK hat beim Vergleich entweder den Wert der Konstanten TRUE oder den von FALSE, und keinen Anderen. Als Fehlerursache im Zusammenhang mit der Ausgangsfrage kommt der true-Vergleich daher m.E. nicht in Betracht. Oder irre ich mich, das wollte ich eigentlich nur wissen :mrgreen:

Bei einer reinen Compilierung würde ich dir zustimmen, nicht aber wenn die Optimierung eingeschaltet ist. Diese optimierte das ganze um und zwar so, dass eventuelle Zuweisungen von irgendwelchen Konstanten (z.B. true) einfach ein Dekrement ausgeführt wird. Und schon kommt es zu einem solchen Verhalten. Um allen möglichen Fehlerquellen aus dem Weg zu gehen bietet sich die einfache Lösung an, niemals direkt auf true/false zu vergleichen und einfach mit NOT bzw. ohne in der Bedingung zu arbeiten. Delphi definiert dies ja eh explizit, dass der Vergleichsterm wahr ergeben muss, damit der nachfolgende Teil ausgeführt wird (bzw. im anderen Falle der Else-Zweig).

p80286 6. Jun 2008 10:52

Re: StreamWrite erzeugt nur 0 Byte-File
 
@Muetze1

danke! das ist mal eine gute Erklärung! Werd es mir hinter die Ohren schreiben.

In dem Zusammenhang eine Frage:

Besteht zwischen
Delphi-Quellcode:
bvar:=(a and b);
if bvar then ....
und
Delphi-Quellcode:
if (a and b) then bvar:=true
else bvar:=false;
if bvar then ...
ein Unterschied?
Ich vermute mal die erste Version ist zu bevorzugen.

Gruß
K-H

mkinzler 6. Jun 2008 10:55

Re: StreamWrite erzeugt nur 0 Byte-File
 
Benötigst du bvar anschliessend noch?
Dann natürlich die erstere

Muetze1 6. Jun 2008 10:58

Re: StreamWrite erzeugt nur 0 Byte-File
 
Zitat:

Zitat von p80286
In dem Zusammenhang eine Frage:

Besteht zwischen
Delphi-Quellcode:
bvar:=(a and b);
if bvar then ....
und
Delphi-Quellcode:
if (a and b) then bvar:=true
else bvar:=false;
if bvar then ...
ein Unterschied?

Hier wäre es wirklich Geschmackssache, ob nun eine einzeilige Evaluierung genutzt wird oder es ausformuliert wird. Grundsätzlich ist ersteres für den Optimierer leichter zu erfassen (und zu optimieren), weil der zweite Konstrukt umfangreich ist und mit Konstantenzuweisungen arbeitet.

Da du die Variable direkt danach in einer Bedingung benutzt, würde ich es direkt auswerten in der If Anweisung, ausser die Variable wird noch woanders benötigt, dann würde ich persönlich die einzeilige Evaluation benutzten. Aber wie gesagt, das wäre hier wirklich Geschmackssache - im Gegensatz hierzu:

Delphi-Quellcode:
bvar := (a and b) = true;
Hier würde dann wieder das zuvor geschriebene gelten, vor allem da dies nur bei boolschen Variablen a und b klappen sollte und bei allen anderen Ordinaltypen als a und b zu einer binären AND Operation führen und nicht zu einer logischen Evaluierung...

p80286 6. Jun 2008 12:10

Re: StreamWrite erzeugt nur 0 Byte-File
 
Vielen Dank euch beiden!

Gruß
K-H

DevidEspenschied 17. Jun 2008 11:23

Re: StreamWrite erzeugt nur 0 Byte-File
 
Zitat:

Zitat von Muetze1

Delphi-Quellcode:
function DateiSchreiben(const AFilename: string; const AFileSize: Int64);

...
      lWritten := lStream.WriteBuffer(lFileData[1], Min(lBytesToWrite, coBlockSize));
...

Wenn ich Deine Funktion "DateiSchreiben" direkt übernehmen möchte, gibt es 2 Probleme. Das erste ist, dass der eigentliche Rückgabewert der Funktion während der Deklaration fehlt, es müsste also heißen:

Delphi-Quellcode:
function DateiSchreiben(const AFilename: string; const AFileSize: Int64) : Boolean;
Das 2. Problem ist die Zeile mit WriteBuffer, wobei mir Delphi jedesmal den folgenden Compilierungsfehler meldet:

E2010: Inkompatible Typen: 'Integer' und 'procedure, untyped pointer or ununtyped parameter'.

Wobei Du von lFileData immer nur das erste Zeichen verwendest. Angenommen ich habe hier einen String mit 64 unterschiedlichen Zeichen, dann würde die Verwendung des ausschließlich 1. Zeichens falsche Ergebnisse liefern.

Muetze1 17. Jun 2008 11:27

Re: StreamWrite erzeugt nur 0 Byte-File
 
Zitat:

Zitat von devidespe
Wenn ich Deine Funktion "DateiSchreiben" direkt übernehmen möchte, gibt es 2 Probleme. Das erste ist, dass der eigentliche Rückgabewert der Funktion während der Deklaration fehlt, es müsste also heißen:

Nun gut, vergessen - passiert. Ist schliesslich hier im Editor getippt und nicht in einem Projekt...

Zitat:

Zitat von devidespe
Wobei Du von lFileData immer nur das erste Zeichen verwendest. Angenommen ich habe hier einen String mit 64 unterschiedlichen Zeichen, dann würde die Verwendung des ausschließlich 1. Zeichens falsche Ergebnisse liefern.

Hä? Ich gebe das erste Zeichen an - bzw. dessen Adresse, da WriteFile() einen untypisierten Var Parameter hat. Somit schreibe ich, wenn ich hinten nur eine 1 bei der Größe angeben würde, ein Zeichen, ja - aber ich gebe mehr als 1 bei der Grösse an und somit schreibt er auch alle nachfolgenden Daten und nach dem 1. Zeichen im String folgenden die anderen.

DevidEspenschied 17. Jun 2008 11:35

Re: StreamWrite erzeugt nur 0 Byte-File
 
Zitat:

Zitat von Muetze1
Hä? Ich gebe das erste Zeichen an - bzw. dessen Adresse, da WriteFile() einen untypisierten Var Parameter hat.

Du meinst bestimmt WriteBuffer, da ich kein WriteFile entdecken kann.

Wie erklärst Du Dir die Fehlermeldung beim Compilieren ?

Übrigens nette Signatur, ich hätte aber an 3 Stellen jeweils 20h eingefügt.

Muetze1 17. Jun 2008 11:38

Re: StreamWrite erzeugt nur 0 Byte-File
 
Zitat:

Zitat von devidespe
Du meinst bestimmt WriteBuffer, da ich kein WriteFile entdecken kann.

Ja, ich meinte Delphi-Referenz durchsuchenWriteBuffer().

Zitat:

Zitat von devidespe
Wie erklärst Du Dir die Fehlermeldung beim Compilieren ?

Na das Delphi-Referenz durchsuchenWriteBuffer() eine Procedure ist und keine Funktion. Und da liegt auch der Fehler: Delphi-Referenz durchsuchenWriteBuffer() gibt keine Anzahl der geschriebenen Bytes zurück, sondern versucht immer die angegebene Anzahl im Stück zu schreiben. Delphi-Referenz durchsuchenWrite() kehrt direkt zurück und gibt die Anzahl der geschriebenen Bytes an. Da hatte ich also die falsche Methode im Kopf.

Ich habe es eben nochmal schnell korrigiert in Delphi und getestet:

Delphi-Quellcode:
function DateiSchreiben(const AFilename: string; const AFileSize: Int64): boolean;
const
  coBlockSize = 16384;
var
  lStream: TStream;
  lFileData: string;
  lBytesToWrite: Int64;
  lWritten: Integer;
begin
  result := false;

    // Erstmal den Stream öffnen. Wenn das nicht klappt, brauch ich kein Array...
  lStream := TFileStream.Create(AFilename, fmCreate or fmShareDenyWrite);
  try
    lFileData := StringOfChar(#55, coBlockSize);

    lBytesToWrite := AFileSize;
    while ( lBytesToWrite > 0 ) do
    begin
      lWritten := lStream.Write(lFileData[1], Min(lBytesToWrite, coBlockSize));

      if lWritten <> Min(lBytesToWrite, coBlockSize) then
        exit;

      Dec(lBytesToWrite, lWritten);
    end;

    result := true;
  finally
    lStream.free;
  end;
end;
Zitat:

Zitat von devidespe
Übrigens nette Signatur, ich hätte aber an 3 Stellen jeweils 20h eingefügt.

Da ich schon sehr gut Hex so lesen kann (wie manche andere auch), wäre das zu einfach gewesen, da 0x20 zu auffällig sind. So weiss man erstmal nicht ob es was sinniges etc ist.

shmia 17. Jun 2008 11:49

Re: StreamWrite erzeugt nur 0 Byte-File
 
WriteBuffer hat den Vorteil, dass man sich selbst um nichts kümmern muss.
WriteBuffer erzeugt eine Exception, wenn das Zielmedium die Daten nicht aufnehmen kann (z.B. Platte voll oder Schreibfehler wg. defektem Sektor)
Delphi-Quellcode:
procedure TStream.WriteBuffer(const Buffer; Count: Longint);
begin
  // wenn Count=0 ist, passiert gar nichts
  // andernfalls werden Count Bytes geschrieben
  if (Count <> 0) and (Write(Buffer, Count) <> Count) then
    // falls beim Schreiben ein Fehler aufgetreten ist, wird eine Exception erzeugt
    raise EWriteError.CreateRes(@SWriteError);
end;
Deshalb empfehle ich WriteBuffer anstelle von Write zu verwenden.

Muetze1 17. Jun 2008 11:53

Re: StreamWrite erzeugt nur 0 Byte-File
 
Nun gut, dann umgestellt um die Exception von WriteBuffer() zu nutzen...

Delphi-Quellcode:
function DateiSchreiben(const AFilename: string; const AFileSize: Int64): boolean;
const
  coBlockSize = 16384;
var
  lStream: TStream;
  lFileData: string;
  lBytesToWrite: Int64;
  lWriteCount: Int64;
begin
  result := false;

    // Erstmal den Stream öffnen. Wenn das nicht klappt, brauch ich kein Array...
  lStream := TFileStream.Create(AFilename, fmCreate or fmShareDenyWrite);
  try
    lFileData := StringOfChar(#55, coBlockSize);

    lBytesToWrite := AFileSize;
    while ( lBytesToWrite > 0 ) do
    begin
      lWriteCount := Min(lBytesToWrite, coBlockSize);

      lStream.WriteBuffer(lFileData[1], lWriteCount);

      Dec(lBytesToWrite, lWriteCount);
    end;

    result := true;
  finally
    lStream.free;
  end;
end;
Ja, wieder im Beitragseditor geschrieben und ungetestet. Also keine Schläge, wenn wieder Kleinigkeiten falsch sind...

DevidEspenschied 17. Jun 2008 11:54

Re: StreamWrite erzeugt nur 0 Byte-File
 
Ja, das war wohl der ausschlaggebende Punkt.

Nun habe ich aber das Problem, nicht etwa einen String wie lFileData mit identischen Zeichen (hier #55, also die Ziffer 7), sondern einem Testmuster schreiben zu wollen. Hierfür habe ich ein 64 Zeichen großes Muster defininiert:

Delphi-Quellcode:
CONST TestMuster = '#abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+';
Wenn ich nun aber TestMuster anstatt von lFileData[1] in der Write-Funktion verwende, wird das Muster nur einmal geschrieben, und der Rest ist Datensalat. Wie könnte man das Beispiel denn abändern, so dass dieses Testmuster fortlaufend geschrieben wird ? Mit 64 Byte kann ich da bspw. Dateigrößen von 1, 4 oder 16 MByte abdecken, da diese durch 64 ohne Rest teilbar sind.

Muetze1 17. Jun 2008 11:59

Re: StreamWrite erzeugt nur 0 Byte-File
 
Der jetzige Code deckt alle Dateigrößen ab, auch ungerade :warn:

Delphi-Quellcode:
function DateiSchreiben(const AFilename: string; const AFileSize: Int64): boolean;
const
  coFileData = '#abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+';
var
  lStream: TStream;
  lBytesToWrite: Int64;
  lWriteCount: Int64;
begin
  result := false;

    // Erstmal den Stream öffnen. Wenn das nicht klappt, brauch ich kein Array...
  lStream := TFileStream.Create(AFilename, fmCreate or fmShareDenyWrite);
  try
    lBytesToWrite := AFileSize;
    while ( lBytesToWrite > 0 ) do
    begin
      lWriteCount := Min(lBytesToWrite, length(coFileData));

      lStream.WriteBuffer(coFileData, lWriteCount);

      Dec(lBytesToWrite, lWriteCount);
    end;

    result := true;
  finally
    lStream.free;
  end;
end;
Wieder ungetestet, aber sollte so klappen.

DevidEspenschied 17. Jun 2008 12:39

Re: StreamWrite erzeugt nur 0 Byte-File
 
Danke, das Beispiel funktioniert einwandfrei.

Nun habe ich bspw. den Fall, die zuvor geschriebenen Testmusterdaten wieder lesen und überprüfen zu wollen, wozu ich einerseits die Create-Zeile wiefolgt abändere:

Delphi-Quellcode:
lStream := TFileStream.Create(AFilename, fmOpenRead);
und andererseits eine lokale Stringvariable namens ReadMuster deklariere sowie ReadBuffer einsetze:

Delphi-Quellcode:
CONST TestMuster = '#abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+';
VAR ReadMuster : STRING;
BEGIN
...
DateiStream.ReadBuffer(ReadMuster, lReadCount);
IF ReadMuster <> TestMuster THEN ShowMessage('Gelesenes Muster stimmt nicht mit dem Testmuster überein.');
...
Beim Debuggen wird diese Funktion auch durchgeführt, aber ich kann auf den String ReadMuster nicht zugreifen (hier erscheint eine Exception). Das beginnt mit der IF-Abfrage direkt nach ReadBuffer. Wie kann man das am besten lösen ?

Muetze1 17. Jun 2008 13:38

Re: StreamWrite erzeugt nur 0 Byte-File
 
Der String ist leer. Setze die entsprechende Stringlänge bevor du was einliest. Delphi-Referenz durchsuchenSetLength

DevidEspenschied 17. Jun 2008 15:25

Re: StreamWrite erzeugt nur 0 Byte-File
 
Also richtig will das noch nicht funktionieren. Mein bisheriger Code:

Delphi-Quellcode:
VAR  DateiStream : TStream;
      lBytesToRead,
      lReadCount  : Int64;
      ReadMuster  : STRING[64];
CONST TestMuster = '#abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+';
BEGIN
  TRY
    TRY
      DateiStream:=TFileStream.Create(LW+':\'+Datei, fmOpenRead);
      IF DateiStream.Size MOD Length(TestMuster) <> 0 THEN Result:=False ELSE
      BEGIN
        lBytesToRead:=SizeInMByte * 1024 * 1024;
        WHILE (lBytesToRead) > 0 DO
        BEGIN
          lReadCount:=Min(lBytesToRead, Length(TestMuster));

          SetLength(ReadMuster, 64);
          ReadMuster:='';
          DateiStream.ReadBuffer(ReadMuster, lReadCount);

          IF ReadMuster<> TestMuster THEN Result:=False;

          Dec(lBytesToRead, lReadCount);

          Application.ProcessMessages;
        END;
      END;
    FINALLY
      DateiStream.Free;
    END;
  EXCEPT
    Result:=False;
  END;
END;

Muetze1 17. Jun 2008 15:59

Re: StreamWrite erzeugt nur 0 Byte-File
 
Du lässt dir auch deinen Stack überschreiben. Es wurde zuvor explizit das erste Zeichen bei Strings angegeben - aus gutem Grund. Du machst dies nicht und übergibst damit die Adresse der Stringvariablen auf deinem Stack und lässt dir diesen mit den Dateidaten vollkippen.

Und nochwas: Wenn du den String auf eine Länge bzw. Grösse von 64 Bytes setzt um ihn dann wieder auf eine Länge von 0 (Leerstring) zurück zu setzen, macht irgendwie, wenn man dies so liest in diesem Satz, wenig Sinn. Weil im Endeffekt hast du dann wieder 0 Bytes für deine Dateidaten.

BTW: Schonmal aufgefallen, dass deine Funktion niemals True zurück gibt und du eigentlich sogar einen Hinweis von dem Compiler bekommst, dass der Rückgabewert der Funktion undefiniert sein könnte?

DevidEspenschied 17. Jun 2008 16:11

Re: StreamWrite erzeugt nur 0 Byte-File
 
Zitat:

Zitat von Muetze1
Du lässt dir auch deinen Stack überschreiben. Es wurde zuvor explizit das erste Zeichen bei Strings angegeben - aus gutem Grund. Du machst dies nicht und übergibst damit die Adresse der Stringvariablen auf deinem Stack und lässt dir diesen mit den Dateidaten vollkippen.

Und nochwas: Wenn du den String auf eine Länge bzw. Grösse von 64 Bytes setzt um ihn dann wieder auf eine Länge von 0 (Leerstring) zurück zu setzen, macht irgendwie, wenn man dies so liest in diesem Satz, wenig Sinn. Weil im Endeffekt hast du dann wieder 0 Bytes für deine Dateidaten.

In der Hilfe wird der Rückgabepuffer mit "var Buffer;" deklariert, woraus ich schließe, dass es sich auch um einen String handeln kann. Ich hatte davor mit BlockRead gearbeitet und konnte eben genau 64 Byte einlesen. Hier scheint das komplizierter zu sein.

Wenn ich die brisante Zeile entferne, in welcher der String wieder auf '' gesetzt wird, dann wird tatsächlich etwas ausgelesen. Allerdings nur ca. 35 Zeichen von 64 Zeichen, obwohl lReadCount mit der Min-Berechnung auf 64 Byte festgelegt wird.

Zitat:

Zitat von Muetze1
BTW: Schonmal aufgefallen, dass deine Funktion niemals True zurück gibt und du eigentlich sogar einen Hinweis von dem Compiler bekommst, dass der Rückgabewert der Funktion undefiniert sein könnte?

Ja, vor dem ersten Try-Wort wird Result:=True gesetzt. Das ist hier irgendwie untergegangen.

Muetze1 17. Jun 2008 16:14

Re: StreamWrite erzeugt nur 0 Byte-File
 
Zitat:

Zitat von devidespe
In der Hilfe wird der Rückgabepuffer mit "var Buffer;" deklariert, woraus ich schließe, dass es sich auch um einen String handeln kann. Ich hatte davor mit BlockRead gearbeitet und konnte eben genau 64 Byte einlesen. Hier scheint das komplizierter zu sein.

Achtung! Das ist ein typenloser Parameter und die Strings sind mit Referenzzählung versehen. Diese Analogie kann man nicht so einfach schliessen.

Schonmal mit der Angabe des erstens Zeichens probiert?

DevidEspenschied 17. Jun 2008 16:22

Re: StreamWrite erzeugt nur 0 Byte-File
 
Zitat:

Zitat von Muetze1
Schonmal mit der Angabe des erstens Zeichens probiert?

Tatsächlich, mit folgender Zeile funktioniert es:

Delphi-Quellcode:
DateiStream.ReadBuffer(ReadMuster[1], lReadCount);
Obwohl ich den Sinn, ein Zeichen eines Strings zu verwenden, nicht ganz verstehe. Vielleicht muss ich das auch nicht. Thx for the help.

Muetze1 17. Jun 2008 16:24

Re: StreamWrite erzeugt nur 0 Byte-File
 
Zitat:

Zitat von devidespe
Obwohl ich den Sinn, ein Zeichen eines Strings zu verwenden, nicht ganz verstehe. Vielleicht muss ich das auch nicht. Thx for the help.

Er verwendet nicht das Zeichen an sich, sondern durch den das "VAR" bei der Argument-Deklaration wird ein Zeiger auf das angegebene Element gebildet. Somit wird der Funktion an sich der Zeiger auf das 1. Zeichen des Strings übergeben und damit genau der Speicherplatz, wo die Daten hin sollen.

Du kannst auch alternativ folgendes angeben:
Delphi-Quellcode:
PChar(DeinString)^

DevidEspenschied 18. Jun 2008 15:24

Re: StreamWrite erzeugt nur 0 Byte-File
 
Zitat:

Zitat von Muetze1
Der jetzige Code deckt alle Dateigrößen ab, auch ungerade :warn:

Delphi-Quellcode:
function DateiSchreiben(const AFilename: string; const AFileSize: Int64): boolean;
const
  coFileData = '#abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+';
var
  lStream: TStream;
  lBytesToWrite: Int64;
  lWriteCount: Int64;
begin
  result := false;

    // Erstmal den Stream öffnen. Wenn das nicht klappt, brauch ich kein Array...
  lStream := TFileStream.Create(AFilename, fmCreate or fmShareDenyWrite);
  try
    lBytesToWrite := AFileSize;
    while ( lBytesToWrite > 0 ) do
    begin
      lWriteCount := Min(lBytesToWrite, length(coFileData));

      lStream.WriteBuffer(coFileData, lWriteCount);

      Dec(lBytesToWrite, lWriteCount);
    end;

    result := true;
  finally
    lStream.free;
  end;
end;
Wieder ungetestet, aber sollte so klappen.

Jetzt muss ich hierzu noch einmal eine (möglicherweise unsinnige) Frage stellen:

Wenn ich nun anstatt

Delphi-Quellcode:
const
  coFileData = '#abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+';
coFileData als STRING[64] deklariere und in der ersten Funktionszeile den oben dargestellten Inhalt zuweise, wird mir jedesmal vor dem Text ein @ mit in die Datei geschrieben.

Delphi-Quellcode:
var
  lStream: TStream;
  lBytesToWrite: Int64;
  lWriteCount: Int64;
  coFileData: STRING[64];
begin
  result := false;
  coFileData:='#abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+';
  ...
Woran kann das liegen ?

Namenloser 18. Jun 2008 15:35

Re: StreamWrite erzeugt nur 0 Byte-File
 
@ dürfte der ASCII-Code für 64 sein. Duw irst ja schon bemerkt haben, dass man auf das erste zeichen eines Strings mit s[1] zugreif statt s[0]. Das liegt daran, dass in s[0] die Länge des Strings steht, bei dir also 64.

DevidEspenschied 18. Jun 2008 15:45

Re: StreamWrite erzeugt nur 0 Byte-File
 
Ja, ASCII Code 64 steht für @. Aber da ich diesen ASCII Code nirgendwo angebe, dürfte er theoretisch auch nicht in die Datei geschrieben werden. Oder verstehe ich das was falsch ?

Muetze1 18. Jun 2008 15:49

Re: StreamWrite erzeugt nur 0 Byte-File
 
Die Konstante wird intern als ShortString abgelegt, ist also zum alten Pascal kompatibel. Dort waren die Strings auf 255 Zeichn begrenzt, da das 0. Zeichen die Länge des Strings angegeben hatte. Deshalb ist es bei AnsiStrings auch verboten auf den 0. Index zu zu greifen.

So, da du nun bei der Konstante einen ShortString erhälst und nicht das erste Zeichen angibst, wird hier die Variable an sich komplett gespeichert und die fängt mit diesem Längenbyte an.

BTW: Aus genau diesem Grund werden auch konstante Strings mit einer Länger von über 255 Zeichen vom Compiler angemeckert und müssen mit einem Pluszeichen aufgetrennt werden (in intern einzelne ShortStrings).


Alle Zeitangaben in WEZ +1. Es ist jetzt 11:03 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 by Thomas Breitkreuz