delphi - Delphi 过程中的无限循环

标签 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); 但我不明白为什么它会导致问题。

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

最佳答案

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 block 是错误的。您必须在 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;       

关于delphi - Delphi 过程中的无限循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32064985/

相关文章:

mysql - 在数据库网格中显示 MySql TIMEDIFF()

c++ - 这行C代码是什么意思?

delphi - 确定 TLabel 的宽度

xml - 无法使用 Delphi 中的 XML 绑定(bind)解析 XML 文本中的值

ios - Delphi XE8 Firemonkey iOS 应用程序仅使用 TEdit 就崩溃

multithreading - 无法销毁线程中的 THTTPReqResp 组件

delphi - 如何检查列表框是否为空?

multithreading - Delphi 中的命名线程 - 这是做什么用的?

delphi - 阻止 Delphi 自动错误地添加单位

delphi - 如何获取TSynEdit等TWinControl的水平和垂直滚动条位置?