我正在编写一个使用Delphi 2010对字节序列进行Z85Encoding的过程。所以我写了一个funtiom
function Z85Encode(input, output: TStream): integer
并希望传入一个Stream。接下来我写了一个重载函数
function Z85Encode(b: TBytes): string;
将字节序列写入TBytesStream(与TMemoryStream相同),调用encode函数,然后将编码数据读取到Result字符串。
问题是我发现TStream.Read的行为与文档非常不同,我无法理解。你可以用其他方式来完成这个功能,但我不明白为什么?我可能想知道Delphi如何实现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;
第一个问题,根据文档,Read应该只读取字节数。但是如果我传入TBytes变量,它会读取整个字节数组,但返回读取计数为5.我也想知道为什么我不需要为变量分配内存' a'第一。似乎Stream.Read将为变量分配内存,这是不可取的。
第二个问题,当TStream.Read(c,5),其中c是字节的数组[0..4]。 c数组中的值是一些随机值,而不是97,98,99,100和101。
进一步测试,如果我将TStream.Write更改为
for n := 0 to High(b) do begin
iStream.Write(b[n], 1);
end;
然后Read(a,5)将不会得到任何结果,Length(a)将获得访问冲突,而Read(c,5)将获得正确的结果。
Read和Write似乎都采用开放式参数,并且根据参数类型的不同表现。如果最终要读取或写入变量的字节值,那么行为不同是可以理解的,但似乎它做得更多,然后只是从变量中确定字节内容。然后,Read和Write应该使用相同的变量类型,而不是我期望的。
答案 0 :(得分:5)
您不是在编写数组的内容。您正在写入数组的地址。动态数组是指针。那个指针的值正是你所写的。
要编写数组,必须执行
iStream.Write(b[0], Length(b));
或者
iStream.Write(Pointer(b)^, Length(b));
我更喜欢后者,因为即使启用了范围检查,它也适用于长度为0的数组。
同样从数组中读取。请注意,您有责任首先分配读缓冲区。你没那样做。
SetLength(a, 5);
iStream.Read(Pointer(a)^, Length(a));
最后,最好使用WriteBuffer
和ReadBuffer
,因为它们包含错误检查,确实实际传输了所请求的字节数。