为什么德尔福 TMemoryStream Read TBytes 和 Array of Bytes 不同?



我正在编写一个过程来使用 Delphi 2010 对字节序列进行 Z85 编码。 所以我写了一个函数:

function Z85Encode(input, output: TStream): integer

并期望传入。 接下来我写了一个重载函数

function Z85Encode(b: TBytes): string;

它将字节序列写入TBytesStream(与TMemoryStream相同),调用编码函数,然后将编码数据读取到 Result 字符串中。

问题是我发现TStream.Read的行为与文档非常不同,我无法理解它。 我可以使用其他方式来完成该功能,但我不太明白为什么? 这可能让我想知道德尔福是如何实现TBytes类型的。

为了说明我的问题,我编写了以下测试程序:

procedure Test;
var
iStream: TMemoryStream;
n: integer;
a: TBytes;
b: TBytes
c: array [0..4] of Byte;
begin
b := TEncoding.UTF8.GetBytes('abcdefghij');  // byte from 97 to 106
iStream := TMemoryStream.Create;
try
iStream.Write(b, Length(b));
iStream.Seek(0, soBeginning);
n := iStream.Read(a, 5); // expect only 5 bytes read, but the whole array is back.
ShowMessage(IntToStr(n));  // n is 5
ShowMessage(IntToStr(Length(a)));  // length is 10!!
iStream.Seek(0, soBeginning);
n := iStream.Read(c, 5);   // c contains random number, not 97, 98, ...
ShowMessage(IntToStr(n));  // n is 5
finally
iStream.Free;
end;
end;

第一个问题,根据文档,读取应只读取字节计数。 但是,如果我传入一个TBytes变量,它会读取整个字节数组,但返回读取计数为 5。 我也想知道为什么我不需要先为变量a分配内存。 似乎Stream.Read将为变量分配内存,这是意料之外的。

第二个问题,当TStream.Read(c, 5)c在哪里是array [0..4] of byte时。c数组中的值是某个随机值,而不是979899100101

通过进一步的测试,如果我将TStream.Write更改为

for n := 0 to High(b) do begin
iStream.Write(b[n], 1);
end;

然后Read(a, 5)不会得到任何结果,Length(a)会得到访问冲突,但Read(c, 5)会得到正确的结果。

似乎读取写入都采用开放类型参数,并根据参数类型表现不同。 如果最终是读取或写入变量的字节值,则行为不同是可以理解的,但似乎它所做的不仅仅是确定变量的字节内容。 然后读取写入应该使用相同的变量类型,这不是我期望的。

您没有编写数组的内容。您正在写入数组的地址。动态数组是一个指针。该指针的值就是您正在编写的内容。

要写入数组,您必须执行

iStream.Write(b[0], Length(b));

iStream.Write(Pointer(b)^, Length(b));

我更喜欢后者,因为即使启用了范围检查,它也适用于长度为 0 的数组。

同样地从数组中读取。请注意,您有责任首先分配读取缓冲区。你没有那样做。

SetLength(a, 5);
iStream.Read(Pointer(a)^, Length(a));

最后,最好使用WriteBufferReadBuffer,因为它们会进行错误检查,检查请求的字节数是否实际传输。

最新更新