Delphi 过程中的无限循环
Infinite Loop in Delphi Procedure
我在使用 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);
但我不明白为什么它会导致问题。
这个过程只被调用一次。我的测试数据只有一个entry/data。
任何帮助将不胜感激。
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));
内存流也显得很浪费。为什么要将整个文件读入内存?这会给您的大文件地址 space 带来压力。使用 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;
抱歉,我不能添加评论,因为我是新手和所有 :) 这个回复是基于我对 Clayton 代码的理解以及他对 recordSize 值的评论。
David 的代码循环的原因可能是您正在解释每四个字节 "block" 是一个数字。我假设您的第一个 Stream.Readbuffer 是正确的,并且文件中的前四个字节是一个长度。
现在,除非我弄错了,否则我预计 recordSize 通常会大于 SizeOf(recordSize),我认为它应该是 4(int 的大小)。尽管如此,这条线在这里毫无意义。
根据我之前的假设,SetLength 是正确的。
现在你的 Move 故事遇到了障碍:你读完这篇文章后还没有读过任何东西!所以在搬家之前,你应该有:
bytesRead := Stream.Readbuffer(指针(缓冲区)^, recordSize);
现在您可以检查 EOF:
如果 bytesRead <> recordSize 那么
提高...;
...并将缓冲区移动到某处(如果您愿意):
移动(缓冲区[0],目标[0],记录大小);
并且您可以读取下一个 recordSize 值。
重复直到 EOF。
我在使用 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);
但我不明白为什么它会导致问题。
这个过程只被调用一次。我的测试数据只有一个entry/data。 任何帮助将不胜感激。
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));
内存流也显得很浪费。为什么要将整个文件读入内存?这会给您的大文件地址 space 带来压力。使用 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;
抱歉,我不能添加评论,因为我是新手和所有 :) 这个回复是基于我对 Clayton 代码的理解以及他对 recordSize 值的评论。
David 的代码循环的原因可能是您正在解释每四个字节 "block" 是一个数字。我假设您的第一个 Stream.Readbuffer 是正确的,并且文件中的前四个字节是一个长度。
现在,除非我弄错了,否则我预计 recordSize 通常会大于 SizeOf(recordSize),我认为它应该是 4(int 的大小)。尽管如此,这条线在这里毫无意义。
根据我之前的假设,SetLength 是正确的。
现在你的 Move 故事遇到了障碍:你读完这篇文章后还没有读过任何东西!所以在搬家之前,你应该有: bytesRead := Stream.Readbuffer(指针(缓冲区)^, recordSize);
现在您可以检查 EOF: 如果 bytesRead <> recordSize 那么 提高...;
...并将缓冲区移动到某处(如果您愿意): 移动(缓冲区[0],目标[0],记录大小);
并且您可以读取下一个 recordSize 值。
重复直到 EOF。