Forum: PC-Programmierung Lazarus: Wo ist mein Fehler bei "Blockread"?


von Manfred (Gast)


Lesenswert?

Hallo Leute,

als Lazarus-Anfänger hänge ich fest.

procedure TForm1.Button1Click(Sender: TObject);
const C_FNAME = 'ab.log';
var  log_datei: file;
  x : integer ;

begin

    Assignfile(log_datei, C_FNAME);
    Reset(log_datei,1);
    while not Eof(log_datei) do begin

          blockread(log_datei,x,sizeof(x));

    end;

Einfach die Datai ab.log öffnen, aber es kommt immer Run-Error (100)
Wer kann mir da bitte kurz helfen? Vielen Dank

von yesitsme (Gast)


Lesenswert?

http://www.freepascal.org/docs-html/user/userap4.html

Existiert die Datei?
Zum Lesen geöffnet?
Sind genug Daten drin?
3 Byte drin und 4 Byte lesen ist halt schlecht...

von Erwin D. (Gast)


Lesenswert?

Existiert denn überhaupt die Datei 'ab.log' an dieser Stelle?
Wenn ich mich richtig erinnere, heißt Error 100: "file not found"

von Manfred (Gast)


Lesenswert?

Die Datei existiert, ist 27kb groß. Sie liegt in dem Ordner, in dem das 
Projekt gespeichert wird und auch das Programm hincompeliert wird.

Danke für die Fehlertabelle. Laut meinem bescheidenen Englisch kommt der 
Fehler, wenn ich zu erst das Ende der Datei lesen will, anstatt des 
Anfanges. Aber ich habe nur Quellcode aus dem Internet genutzt, nix 
eigenes erstellt, außer die Variablen umbenannt...

von Jim M. (turboj)


Lesenswert?

Manfred schrieb:
> Die Datei existiert, ist 27kb groß.

Hier wäre die genaue Größe in Bytes entscheidend. Wenn die nicht durch 
4 teilbar ist ergibt sich der 100er Fehler automatisch am Ende.

von Rainer V. (rudi994)


Lesenswert?

Manfred schrieb:
> wenn ich zu erst das Ende der Datei lesen will
1
procedure CodeBlocks();
2
var F: File;
3
  fsiz, xsiz: longint;
4
  x: integer; //Lesepuffer
5
begin
6
  AssignFile(F, 'ab.log');
7
  Reset(F, 1);
8
  fsiz := FileSize(F);
9
  xsiz := Sizeof(x);
10
  while (fsiz >= xsiz) do begin
11
    fsiz := fsiz - xsiz;
12
    Seek(F, fsiz);
13
    BlockRead(F, x, xsiz);
14
    //... x weiter verarbeiten
15
  end;
16
  CloseFile(F);
17
end;

"while not EOF(F) ..." ist beim Rückwärts-Lesen nicht verwendbar, weil 
ja sofort EOF auftritt, wenn Daten am Dateiende zuerst gelesen werden. 
Ist die Dateigröße kein ganzzahliges Vielfaches der Datenblockgröße, 
dann wird der Datenblock am Dateianfang nicht mehr gelesen. LG

von Georg (Gast)


Lesenswert?

Manfred schrieb:
> Laut meinem bescheidenen Englisch kommt der
> Fehler, wenn ich zu erst das Ende der Datei lesen will

Soweit ich weiss geht das mit Reset garnicht, man müsste zuerst die 
Datei öffnen und dann ein FileSeek ans Dateiende.

Georg

von Stefan B. (Gast)


Lesenswert?

Wie ist die Datei genau aufgebaut?
Gib mal bitte ein paar Beispieldaten.

Und was versuchst du aus der Datei auszulesen?

Wie andere vorher schon geschrieben haben, liest du integer-große Blöcke 
aus der Datei, also jeweils 4 Byte groß.

Jim M. schrieb:
> Hier wäre die genaue Größe in Bytes entscheidend. Wenn die nicht durch
> 4 teilbar ist ergibt sich der 100er Fehler automatisch am Ende.

Manfred schrieb:
> Laut meinem bescheidenen Englisch kommt der
> Fehler, wenn ich zu erst das Ende der Datei lesen will, anstatt des
> Anfanges.
Nein, der Fehler kommt daher, dass du versuchst, hinter dem Ende der 
Datei zu lesen.
Nämlich dann, wenn weniger als 4 Byte bis zum Dateiende zu lesen sind.

Um das zu verhindern, verwende folgendes:
1
procedure BlockRead(
2
  var f: file;
3
  var Buf;
4
  count: Word;
5
  var Result: Integer
6
);

Leg dir eine neue Integer-Variable für Result an und setz diese als 4. 
Parameter für deinen BlockRead-Aufruf ein.
Dadurch erhältst du die Anzahl der gelesenen Bytes, und der Error 100 
verschwindet.

Aber irgendwie schon merkwürdig, dass eine Binäre Datendatei im 
richtigen Format eine falsche Dateigröße hat...

Sieht deine Datei so aus:
1
Ò  .
Im Hex-Editor so:
1
D2 04 00 00 2E 16 00 00
Dann würde dein Code folgendes zurückliefern:
1. Schleifendurchlauf: x=1234
2. Schleifendurchlauf: x=5678

Oder sieht deine Datei eher so aus:
1
1234
2
5678
3
0815
4
4711
Im Hex-Editor so:
1
31 32 33 34 0D 0A 35 36 37 38 0D 0A 30 38 31 35 0D 0A 34 37 31 31
Dann wäre deine Methode völlig ungeeignet, denn dein Code würde 
folgendes zurückliefern:
1. Durchlauf: x=875770417
2. Durchlauf: x=909445645
3. Durchlauf: x=168638519
4. Durchlauf: x=892418096
5. Durchlauf: x=926157325
6. Durchlauf: x=926167345
Und ich kann mir irgendwie nicht vorstellen, dass du das wolltest...

von Manfred (Gast)


Lesenswert?

Vielen Dank für die Hilfe. Ich fange dann doch mal von vorne an:
Ich habe eine Datei, binär-Datei, Ascii-Zeichen, die ich auslesen will, 
als in reinster Ur-Form, ohne irgendwelche Schnörkel. Also Byte für 
Byte. Dies soll am besten mit Blockread gehen...stimmt das überhaupt, 
oder gibt es da eine andere Möglichkeit?

von Stefan B. (Gast)


Lesenswert?

Also wenn du byte-weise auslesen willst, muss deine Variable "x" auch 
als Byte deklariert sein, dann bekommst du in jedem Schleifendurchlauf 
auch genau 1 Byte.

Ein Integer ist 4 Byte groß, also liest du mit als Integer definiertem 
"x" 4 Bytes auf einmal, die dann als ein Integer interpretiert werden.

Ändere deine Definition von "x" also zu "Byte" oder "AnsiChar", je 
nachdem was du mit dem Wert machen willst.

BlockRead ist eigentlich dafür gedacht, größere Blöcke auf einmal zu 
lesen, beispielsweise in ein Array.
Du kannst es natürlich trotzdem verwenden, sonderlich performant wird 
das aber nicht.

von Rainer V. (rudi994)


Lesenswert?

Georg schrieb:
> zuerst die Datei öffnen und dann ein FileSeek ans Dateiende.

Ohne Reset(F, 1) gibt es hier eine Exeption (exitcode 217). Mit Reset() 
nach Assign() wird die Datei in den Lese-Modus gesetzt. Danach kann mit 
Seek() der Dateizeiger gesetzt werden, Seek(F, 0) ist der Dateianfang, 
Seek(F, FileSize(F)-1) ist das letzte Byte bzw. Zeichen am Dateiende.

Manfred schrieb:
> Byte für Byte

"var x: Byte;" anstatt "var x: integer;", aber nicht "var x: char;", 
weil mit einem untypisierten Dateiobjekt "var f: file" gearbeitet wird.

von Amateur (Gast)


Lesenswert?

Ich finde es immer wieder toll, wenn Leute, die nach eigenen Angaben 
Anfänger sind, keine Überprüfungen brauchen...
Man überprüft beim Öffen ob OK.
Man überprüft beim Lesen ob gelesen.
... muss aber nicht sein ...

Ob Reset(log_datei,1); in Ordnung war, kann man Überprüfen.
Ob blockread(log_datei,x,sizeof(x),gelesen); in Ordnung ist kann man...
Die Schreiber der Funktionen/Prozeduren haben es zumindest vorgesehen.

von Kai (Gast)


Lesenswert?

Nimm mal nen absoluten Pfad, statt nen relativen. Das CWD kann ein 
anderes sein, als erwartet.

von Robert L. (lrlr)


Lesenswert?

> Lazarus: Wo ist mein Fehler bei "Blockread"?

der Fehler ist, Blockread überhaupt zu verwenden..

nimm TFileStream oder ähnliches..

von Rainer V. (rudi994)


Lesenswert?

Robert L. schrieb:
> der Fehler ist, Blockread überhaupt zu verwenden.

Er hätte z.B. "var F: file of byte; x: byte;" und dann "read(F, x);" 
anstatt BlockRead() nehmen können. Das ganze Zeugs gab es ja schon in 
TPascal unter DOS. Lazarus habe ich nicht, nehme aber einfach mal an, 
daß sich der TE für einen Umstieg auf TFileStream erstmal eingehend mit 
entsprechenden Klassen und Objekten beschäftigen müßte.

von Bernd K. (prof7bit)


Lesenswert?

Robert L. schrieb:
> der Fehler ist, Blockread überhaupt zu verwenden..
>
> nimm TFileStream oder ähnliches..

+1

Warum musste es über 13 Postings dauern bis diese naheliegende Antwort 
auftauchte?

von Bernd K. (prof7bit)


Lesenswert?

Rainer V. schrieb:
> nehme aber einfach mal an,
> daß sich der TE für einen Umstieg auf TFileStream erstmal eingehend mit
> entsprechenden Klassen und Objekten beschäftigen müßte.

Die drei Methoden von TFileStream zu lernen die er braucht ist auch 
nicht komplizierter als sich mit den drei oder vier verstaubten file 
Funktionen auseinanderzusetzen die er vielleicht braucht, nur daß er um 
ersteres eh nicht herumkommen wird und letzteres heute eigentlich nicht 
mehr verwendet wird.

von GeraldB (Gast)


Lesenswert?

Hallo Manfred,

bevor du versuchst auf die Datei zuzugreifen solltest du erstmal mal 
prüfen lassen, ob dein Programm die Datei überhaupt finden kann:
1
if not FileExists(C_FNAME) then
2
   begin
3
      readln('Datei ''',C_FNAME,''' nicht gefunden !');
4
      halt(1)
5
   end;
Wie oben bereits erwähnt wurde mußt du die Variable x als Byte 
definieren, da Integer länger als ein Byte ist.

Manfred schrieb:
> Laut meinem bescheidenen Englisch kommt der
> Fehler, wenn ich zu erst das Ende der Datei lesen will, anstatt des
> Anfanges.
Nein, du hast falsch übersetzt. Der Fehler kommt wenn man hinter dem 
Dateiende weiter liest.

Stefan B. schrieb:
> Ein Integer ist 4 Byte groß, also liest du mit als Integer definiertem
> "x" 4 Bytes auf einmal, die dann als ein Integer interpretiert werden.
Das stimmt nicht. Die Länge von Integer ist nicht fest definiert. 
Abhängig vom System kann Integer 2 oder 4 Byte lang sein.

von Rainer V. (rudi994)


Lesenswert?

Bernd K. schrieb:
> verstaubten file Funktionen ...
> heute eigentlich nicht mehr verwendet wird.

Ist bei mir auch öfter so, daß der AV-Scanner anschlägt, wenn ich den 
verstaubten Kram in FPC kompiliere (passiert allerdings auch beim 
Kompilieren von C/C++ Konsolenprogrammen unter CodeBlocks mit MinGW).

von Bernd K. (prof7bit)


Lesenswert?

1
procedure TForm1.Button1Click(Sender: TObject);
2
const
3
  C_FNAME = 'ab.log';
4
5
var
6
  FS: TFileStream;
7
  X: DWord;
8
9
begin
10
  try
11
    FS := TFileStream.Create(C_FNAME, fmOpenRead);
12
    Memo1.Append('file is open for reading');
13
14
    try
15
      while FS.Position + SizeOf(X) <= FS.Size do begin
16
        FS.Read(X, SizeOf(X));
17
        Memo1.Append(Format('Read: %0.8x', [X]));
18
      end;
19
      Memo1.Append('no more data to read');
20
21
    except
22
      Memo1.Append('Error while trying to read from file');
23
    end;
24
25
    Memo1.Append('closing file');
26
    FS.Free;
27
28
  except
29
    Memo1.Append('could not open File');
30
  end;
31
end;

von GeraldB (Gast)


Lesenswert?

Hallo Manfred,

noch ein kleiner Nachtrag.
Bei sauberer Programmierung muß die Datei auch wieder geschlossen 
werden:
1
close(C_FNAME);

von Stefan B. (Gast)


Lesenswert?

GeraldB schrieb:
> Stefan B. schrieb:
>> Ein Integer ist 4 Byte groß, also liest du mit als Integer definiertem
>> "x" 4 Bytes auf einmal, die dann als ein Integer interpretiert werden.
> Das stimmt nicht. Die Länge von Integer ist nicht fest definiert.
> Abhängig vom System kann Integer 2 oder 4 Byte lang sein.
Ich komme von Delphi, und da ist ein Integer 4 Byte lang. Immer.
Deshalb nahm ich an, dass wäre bei Lazarus/FreePascal genauso, wäre ja 
auch sinnvoll.

von GeraldB (Gast)


Lesenswert?

Stefan B. schrieb:
> Ich komme von Delphi, und da ist ein Integer 4 Byte lang. Immer.
> Deshalb nahm ich an, dass wäre bei Lazarus/FreePascal genauso, wäre ja
> auch sinnvoll.

In den Handbüchern von FreePascal und Lazarus steht dazu folgendes:
1
The 'integer' type maps to the smallint type in the default Free Pascal mode. It maps to either a
2
longint in either Delphi or ObjFPC mode. The 'cardinal' type is currently always mapped to the
3
longword type.

Die Länge von Integer ist im Standard Pascal nicht festgelegt. Üblich 
sind 2 oder 4 Byte.
Die Länge von Integer ist auch abhängig von der Datenbusbreite der CPU 
und vom Betriebssystem.

Als ich vor 35 Jahren auf dem Apple 2 Pascal (UCSD Pascal und Turbo 
Pascal unter CP/M mit Z80-Karte) gelernt habe, war Integer 2 Byte 
lang. Und laut einem Buch zu Turbo Pascal 6 für PC war Integer auch 
nur 2 Byte lang. Und irgendwann hat dann Borland bei Delphi auf 4 Byte 
umgestellt. Und evtl. kommt ja Embarcadero irgendwann noch auf die Idee 
für 64-bit Programme auf 8 Byte zu verlängern.

von Rainer V. (rudi994)


Lesenswert?

Stefan B. schrieb:
> Delphi, und da ist ein Integer 4 Byte lang. Immer.

In FPC auch, sofern {$MODE OBJFPC} deklariert ist (hier unter Win7 x64). 
Sonst nur 2 Byte, wie bei Smallint, welches den 16-Bit Integer-Typ aus 
dem früheren TPascal für DOS ersetzt.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

GeraldB schrieb:
> Und irgendwann hat dann Borland bei Delphi auf 4 Byte umgestellt.

Dieses "irgendwann" war der Zeitpunkt, als Borland einen 32-Bit-Compiler 
'rausbrachte. Das war 1996 mit "Delphi 2".

von Manfred (Gast)


Lesenswert?

Ich habe jetzt etwas probiert und mich für die TFileStream von Bernd K. 
entschieden. Die Daten werden zu je 4 Zeichen pro Zeile als 2 Zeichen 
Hex angezeigt...nur leider von hinten nach vorne, also 4tes 3tes 2tes 
1tes Zeichen. Könnte man das beim Einlesen/Weitergeben an die 
Memokomponente unterbinden? Und kann man das mit TFileStream.Read 
eingelesene nicht gleich in richtiger Reihenfolge einem string 
übergeben, bei Beibehaltung der "Hex-Darstellung"?

von Manfred (Gast)


Lesenswert?

Nachtrag: Sämliche Bemühungen scheitern dort, daß die 
inttostr(x)-Funktion wieder aus dem Hex-Code ein normales Ascii-Zeichen 
macht, welches man wieder per Hand mit inttohex (char) konvertieren 
müsste.
Ein Array mit Char anlegen und TFileStream.Read per Pointer zuweisen und 
dann den Index invertieren? Bin da etwas ratlos..

von GeraldB (Gast)


Lesenswert?

Hallo Manfred,

das Programm von Bernd K. kann auch gar nicht richtig funktionieren, da 
er dort den 4 Byte langen Datentyp Dword verwendet. Vereinfacht 
ausgedrückt: Wenn ein ganzzahliger Datentyp länger als 1 Byte ist, 
werden für x86-Prozessoren die Bytes in umgekehrter Reihenfolge im RAM 
abgelegt. Daher die Drehung der Daten die du festgestellt hast.

Wir stochern hier im dunklen, da du keine Infos zum Aufbau deiner Datei 
nennst und auch nicht sagst wie die Ausgabe der Daten vom Programm 
formatiert sein soll.

von Bernd K. (prof7bit)


Lesenswert?

GeraldB schrieb:
> das Programm von Bernd K. kann auch gar nicht richtig funktionieren,

Definiere "richtig".

Ich habe mich am Originalbeispiel des OP orientiert das nahelegt er habe 
longint little endian in der Datei.

Außerdem wird nichts in umgekehrter Reihenfolge im RAM angelegt, es wird 
so abgelegt wir es kommt, es wird nur bei der Interpretation als Zahl 
als LE interpretiert. Der OP soll sich mal zu Endianness schlau lesen.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.