Delphi过程中的无限循环

时间:2015-08-18 06:24:05

标签: delphi delphi-xe8

我在使用Delphi的TMemoryStream(或TFileStream)方面遇到了一个奇怪的问题。在将流的一部分读入字节数组时。这里以一些代码为例。

procedure readfromstream();
var
     ms : TMemoryStream;
     buffer : array of byte;
     recordSize : Integer;
begin
  try
  begin
     ms := TMemeoryStream.Create();
     ms.LoadFromFile(<some_path_to_a_binary_file>);

     while ms.Position < ms.Size do
     begin
         buffer := nil;
         SetLength(buffer, 4);
         ms.ReadBuffer(buffer, 4);
         move(buffer[0], recordSize, 4);

         SetLength(buffer, recordSize);
         ms.Position := ms.Position - 4;           // Because I was having issues trying to read the rest of the record into a specific point in the buffer
         FillChar(buffer, recordSize, ' ');
         ms.ReadBuffer(buffer, recordSize);        // Issue line ???

         // Create the record from the buffer
     end;
  finally
  begin
     ms.Free();
  end;
end;

程序被称为,

// Some stuff happens before it

readfromstream();

// Some stuff happens after it

在调试时,我可以看到它将流读入缓冲区并且记录正确地存储在内存中。然后程序正常退出,调试器退出程序,但我最终直接回到程序中并重复。

通过强制程序提前退出我相信问题涉及ms.ReadBuffer(buffer, recordSize);,但我不明白为什么会导致问题。

此过程仅调用一次。我的测试数据只有一个条目/数据。 任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:5)

FillChar(buffer, recordSize, ' ');

在这里,您将覆盖动态数组变量(指针),而不是写入数组的内容。这会导致内存损坏。几乎任何事情都发生在那一点上。

无论如何,对FillChar的调用是不必要的。无论如何,你将要读入整个阵列。移除对FillChar的呼叫。

为了将来参考,要正确地进行调用,您可以这样写:

FillChar(Pointer(buffer)^, ...);

FillChar(buffer[0], ...);

我更喜欢前者,因为后者在数组长度为零时会受到范围错误的影响。

然后

ms.ReadBuffer(buffer, recordSize);

犯了完全相同的错误,写入数组变量而不是数组,从而破坏内存。

那应该是

ms.ReadBuffer(Pointer(buffer)^, recordSize);

ms.ReadBuffer(buffer[0], recordSize);

循环内的前4行是笨拙的。直接读入变量:

ms.ReadBuffer(recordSize, SizeOf(recordSize));

我建议您对您阅读的recordSize的值执行一些完整性检查。例如,任何小于4的值显然都是错误的。

将流指针移回并再次读取并不是很重要。您可以将recordSize复制到第一个4字节和数组中,然后阅读其余部分。

Move(recordSize, buffer[0], SizeOf(recordSize));
ms.ReadBuffer(buffer[SizeOf(recordSize)], recordSize - SizeOf(recordSize));

内存流似乎也很浪费。为什么要将整个文件读入内存?这会给你的大文件地址空间带来压力。使用buffered file stream

让调用者分配流将为调用者提供更大的灵活性。然后,他们可以从任何类型的流中读取,而不是限制使用磁盘文件。

您的try/finally阻止错误。您必须在try之前获取资源。正如您所拥有的那样,构造函数中的异常会导致您对未初始化的变量调用Free

更好的版本可能是:

procedure ReadFromStream(Stream: TStream);
var
  buffer: TArray<byte>;
  recordSize: Integer;
begin
  while Stream.Position < Stream.Size do
  begin
    Stream.ReadBuffer(recordSize, SizeOf(recordSize));     
    if recordSize < SizeOf(recordSize) then
      raise ...;

    SetLength(buffer, recordSize);
    Move(recordSize, buffer[0], SizeOf(recordSize));
    if recordSize > SizeOf(recordSize) then
      Stream.ReadBuffer(buffer[SizeOf(recordSize)],
        recordSize - SizeOf(recordSize));

    // process record
  end;
end;       

答案 1 :(得分:0)

对不起,我无法添加评论,作为一个新的和所有:)这个回复是基于我对Clayton的代码的理解,根据他对recordSize值的评论。

David的代码循环的原因可能是您解释每个四字节“块”是一个数字。我假设你的第一个Stream.Readbuffer是正确的,文件中的前四个字节是一个长度。

现在,除非我弄错了,我希望recordSize通常大于SizeOf(recordSize),我认为它应该是4(int的大小)。然而,这条线在这里毫无意义。

根据我之前的假设,SetLength是正确的。

现在你的Move就是这个故事遇到障碍的地方:自从你阅读了这篇文章以来,你还没有读过任何东西!所以在搬家之前,你应该: bytesRead:= Stream.Readbuffer(Pointer(buffer)^,recordSize);

现在您可以查看EOF: if bytesRead&lt;&gt;记录大小然后     提高...;

...并将缓冲区移动到某处(如果您愿意): 移动(缓冲区[0],dest [0],recordSize);

您可以阅读下一个recordSize值。

重复直至EOF。