0

I am trying to organize saving and loading of data changing in size. So the save file needs to store several (unknown and every time different number) of dynamic arrays.

The mistake appears in this MCVE:

procedure TAnnMainF.Button6Click(Sender: TObject);
var
  f: file;
  ari, aro: array of double;
  i, Count: word;
begin
  SetLength(aro, random(5) + 1);
  for i := 0 to High(aro) do
    aro[i] := random(2001) / 2000 - 1;

  AssignFile(f, 'c:\delme\1.txt');
  ReWrite(f);
  Count := Length(aro);
  BlockWrite(f, Count, SizeOf(word));
  BlockWrite(f, aro[0], SizeOf(double) * Count);
  CloseFile(f);

  Reset(f);
  BlockRead(f, Count, SizeOf(word));
  BlockRead(f, ari[0], SizeOf(double) * Count);
  CloseFile(f);
end;

This code results in I/O error 998. I was trying to declare the type TDoubleArray = array of Double; and pass ari as a parameter in BlockRead. I also tried to SetLength(ari, Count) before I call BlockRead without any success.

The Answer to this question did not help me. The code reads the Count properly but rises an exception at array loading. What am I doing wrong?

Community
  • 1
  • 1
asd-tm
  • 3,381
  • 2
  • 24
  • 41
  • 1
    You lost `SetLength(ari, Count)` after `BlockRead(....count...)` – kami Nov 03 '16 at 12:04
  • @kami I tried that and mentioned that in my question. – asd-tm Nov 03 '16 at 12:07
  • why not using streams instead? http://stackoverflow.com/questions/9032115/delphi-save-and-load-dynamic-array – RBA Nov 03 '16 at 12:21
  • @RBA Will it be faster? – asd-tm Nov 03 '16 at 12:28
  • In general there is no speed gain. If the file is large, a buffered input can be more responsive. – LU RD Nov 03 '16 at 12:30
  • @asd-tm - as Lu RD answered, there is no major speed difference, but you should take into account the warning from the answer. – RBA Nov 03 '16 at 12:37
  • 1
    The IO Error 998 (origin in the OS) means `ERROR_NOACCESS` with description `Invalid access to memory location`. The reason in your presented code is that you don't set the length of `ari`. The reason for the error even if you set the length, is that you attempt to read more than you think because of the code error that @LURD pointed out. – Tom Brunberg Nov 03 '16 at 12:46
  • Why are you deleting questions once you have the answers? Isn't that a waste of our time? – David Heffernan Nov 07 '16 at 07:44
  • @DavidHeffernan I do not understand the reason for downvoting that question. It did not violate the rules of the forum and contained only MCVE. The SO forum encourages the users to keep the good record of Q and O and not to collect bad Q in its database. That was the reason for deleting the question. I also do not understand the reason for downvoting this question by you as well. Such an activity threatens the commmon users to ask questions on Delphi and thus decreases interest to the environment. Finally it brings financial harm to Embarcadero.Anyway I guess you have priveleges to undelete it – asd-tm Nov 07 '16 at 08:00
  • No I can't undelete. I just wanted my time. And you gave away the opportunity to get more help than I gave you. Thanks a lot. I don't really follow your points about financial harm to Emba. I just asked why you removed your question, and with it my answer. – David Heffernan Nov 07 '16 at 08:08
  • @DavidHeffernan But I wonder why did you downvote these two Q? If the Q was bad then why to store it in DB? – asd-tm Nov 07 '16 at 08:09
  • 1
    I've no idea who voted on any of your posts. Voting is anonymous. I don't understand why you would remove my answer after after I spent my time. That's very ungrateful. That's all. Good luck sorting out your busy loops. – David Heffernan Nov 07 '16 at 08:13

1 Answers1

5

You must set the size of the block in the ReWrite/Reset commands:

ReWrite(f,1);
...
Reset(f,1);

From documentation:

RecSize is an optional expression that can be specified only if F is an untyped file. If F is an untyped file, RecSize specifies the record size to be used in data transfers. If RecSize is omitted, a default record size of 128 bytes is assumed.

This means that reading the data will overflow the allocated buffer, hence the I/O error from the system.

Also read this warning about using ancient file I/O BlockRead/BlockWrite:

Warning: This is an older method that is particularly dangerous to use because of the untyped Buf parameter, leading to potential memory corruption. The record size used by BlockRead and BlockWrite is governed by the optional 2nd parameter to the Reset or Rewrite call that was used to open the file being written. It is preferable to use streams in your applications. For example, a user procedure involving a stream can use both TMemoryStreams and TFileStreams, instead of being limited to using files as with these older routines.

In general the speed difference between BlockRead/Write and streams is insignificant. For larger files, a buffered handler is preferred.

There is an excellent example of a buffered file stream handler from David: Buffered files (for faster disk access)


As @kami/@TomBrunberg noted and what you tried, you must also allocate the length of the ari dynamic array before reading the data.

Community
  • 1
  • 1
LU RD
  • 34,438
  • 5
  • 88
  • 296
  • Thank you, very complete answer! Please, append your comment about the speed to the answer so that the community could read it as well. – asd-tm Nov 03 '16 at 12:33