2

i'm getting an I/o 998 error, my task is to rewrite numbers from file to array, and find max and min values. What i'm doing wrong ?

implementation

var
  f2: file of Real;
  m: array of Real;

procedure TForm1.Button1Click(Sender: TObject);
var 
  f: Real;
  max, min: Real;
  i, j: Integer;
begin
  AssignFile(F2, 'test3.dat');
  Rewrite(f2);

  for i := 1 to 50 do
  begin
    f := RandomRange(-100, 100);
    Randomize;
    Write(f2, f);
  end;

  CloseFile(f2);

  i := 0;

  Reset(f2);

  while not Eof(f2) do
  begin
    SetLength(m, i);
    Read(f2, m[i]);
    Inc(i);
  end;

  CloseFile(f2);

  max := m[1];
  min := m[1];

  for j := 1 to i do
    if m[j] > max then
      max := m[j]
    else 
    if m[j] < min then
      min := m[i];
TLama
  • 75,147
  • 17
  • 214
  • 392
  • 1
    Not posting all your code. Calling Randomise every time round the loop will destroy your randomness. Pascal I/O? Why? Out of bounds array access cannot help. Incorrect indexing on min/Max loop also bad news. Frankly, this code is a disaster. – David Heffernan Dec 18 '13 at 17:49
  • You should initialize i := 1 and not i:=0 otherwise when you do Setlength(m, i) you will always have an array 1 element smaller than what you need. – Giacomo Degli Esposti Dec 18 '13 at 17:50
  • possible duplicate of [block read error](http://stackoverflow.com/questions/9493522/block-read-error) – Im0rtality Dec 18 '13 at 17:52
  • On the very first iteration you are specifically unsetting your dynarray by doing `SetLength(m, 0)` (equivalent to `m := nil {!!}`). This is the cause of your I/O error. And ignore complains about standard Pascal I/O as nonsense. – Free Consulting Dec 18 '13 at 19:49
  • What is "Real" type here ? Is it "double" or "Real48" ? And what does the file consist of, "Real48" or "double" ? In Delphi there si nop real "real" type, only a stub, whic hcan be connected to different actual types. – Arioch 'The Dec 19 '13 at 10:03

2 Answers2

2

Many errors, see comments in code.

  • Randomize should be called once at program start.
  • Dynamic arrays has start index 0.
  • CloseFile releases file handle
  • Define length of the dynamic array before the loop, otherwise you will get i/O error.
  • High(m) will get the max index of the dynamic array.
  • Index variable for assigning the min value is j.

implementation

var
  f2: file of Real;
  m: array of Real;

procedure TForm1.Button1Click(Sender: TObject);
var 
  f: Real;
  max, min: Real;
  i, j: Integer;
begin
  AssignFile(F2, 'test3.dat');
  Rewrite(f2);

  for i := 1 to 50 do
  begin
    f := RandomRange(-100, 100);
    //Randomize;  <-- Call this once at program start
    Write(f2, f);
  end;

  //CloseFile(f2); <-- Don't close yet.

  Reset(f2);
  SetLength(m, 50);  // <-- Define length of dynamic array
  i := 0;
  while not Eof(f2) do
  begin
    // SetLength(m, i); // <-- Moved to before while loop, or use SetLength(m,i+1);
    Read(f2, m[i]);
    Inc(i);
  end;

  CloseFile(f2);

  max := m[0];  // <-- Dynamic arrays start with index 0
  min := m[0];  // <-- Dynamic arrays start with index 0

  for j := 1 to High(m) do // <- Max index
    if m[j] > max then
      max := m[j]
    else 
    if m[j] < min then
      min := m[j]; // <-- j is correct index variable
LU RD
  • 34,438
  • 5
  • 88
  • 296
  • `CloseFile` is not an error at all, just a redundant call. `CloseFile` releases previously acquired write-only handle. If there would no `CloseFile`, subsequent `Reset` would do the same before acquiring read-write handle. – Free Consulting Dec 18 '13 at 20:29
  • @FreeConsulting, documentation says that `CloseFile` terminates the association between the file variable and the disk file. Furthermore: `The external file associated with F is completely updated and then closed, freeing the file handle for reuse.` So the fact that `Reset()` internally closes the file and swaps to read-write mode is considered implementation detail and best practice here is to play by the rules given by the documentation. – LU RD Dec 18 '13 at 23:21
  • Then the most correct way (to ensure what external file is completely updated) would be to bring [`CloseFile`](http://www.freepascal.org/docs-html/rtl/system/close.html) call back and not to rely in described `Reset` behaviour, right? – Free Consulting Dec 18 '13 at 23:55
  • @FreeConsulting, certainly not. Why rely on a file variable that has been detached from the file? Then you need to call `AssignFile()` again according to the documented behavior. – LU RD Dec 19 '13 at 07:43
  • Exactly. To avoid all of implementation specific stuff, you need to `Close` (completely update and tear down W/O handle association and name association) and then `Assign` back (bind filename) and finally `Reset` (acquire R/W handle). – Free Consulting Dec 19 '13 at 08:09
  • @FreeConsulting, then I don't understand you at all. It is perfectly legal to call `Reset()` on an open file. – LU RD Dec 19 '13 at 08:36
  • Its perfect legal to call `Reset` on `Close`'d file, too. In Delphi implementation `Reset`/`Rewrite` does an implicit `Close` as well as `Close` call does not forget filename. – Free Consulting Dec 19 '13 at 08:51
  • @FreeConsulting, show where this is documented. The file association is lost after a call to `CloseFile()`. Abusing the fact that `File` is a record that may contain valid information after a call to `CloseFile()` is not a recommended practice. – LU RD Dec 19 '13 at 09:07
  • I posted [fpc doc link](http://www.freepascal.org/docs-html/rtl/system/close.html) above. Delphi behaves the same way. So, `CloseFile` call is no more than redundant statement. – Free Consulting Dec 19 '13 at 09:30
2
  i := 0;

  Reset(f2);

  while not Eof(f2) do
  begin
    SetLength(m, i);
    Read(f2, m[i]);
    Inc(i);
  end;

The above code sets the length of a dynamic array to 0 (i) and tries to read into its non-existing element. This causes the RTL to pass an invalid buffer to ReadFile api. The OS returns '0' indicating the function failed and sets the last error to '998' - that's ERROR_NOACCESS. RTL sets the in/out error code and raises it.

As for the answer, use the debugger. Break when the debugger raises an exception. On the next run, put a breakpoint on the faulting statement then trace into code (RTL in this case). Additionally, should you have 'range checking' on in compiler options, you'd get a range check error instead of an I/O error, in which case you would probably see the mistake quickly.

Sertac Akyuz
  • 54,131
  • 4
  • 102
  • 169
  • what is the problem of `i+1`? – ALZ Dec 18 '13 at 18:45
  • @ALZ - I don't understand your question. – Sertac Akyuz Dec 18 '13 at 18:46
  • @SertacAkyuz, in previous comments (removed already) You wrote to @TLama that `i+1` is self-destructing comment - or I understood it wrongly. IMHO here really should be i+1, since 1st time `i:=0` and You set length to 0, then try to red into that array (0-length) – ALZ Dec 18 '13 at 18:49
  • @ALZ - You got it wrong. I asked TLama to not to remove his comment as I intended to remain in the boundary of the question. I also voted for his comment. – Sertac Akyuz Dec 18 '13 at 18:53
  • @SertacAkyuz, OK, sorry for misunderstanding. Thanks – ALZ Dec 19 '13 at 08:30