【发布时间】:2018-04-17 06:36:48
【问题描述】:
我正在编写一个使用 Delphi 2010 对字节序列进行 Z85Encoding 的程序。所以我写了一个函数:
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 是array [0..4] of byte。 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 应该使用相同的变量类型,这不是我的预期。
【问题讨论】:
-
至少在 Delphi2010 中 iStream.Write(b, Length(b));会造成麻烦。而是使用 iStream.Write(b[0], Length(b)); .原因是如果你写 b,则写入(隐藏的)动态数组记录中的数据而不是数据。
-
@mrabat 请注意,在 XE3 及更高版本中,
Write()的重载将TBytes作为输入。 -
我明白了。猜猜我是时候更新到更新版本了;)
-
@mrabat 不是真的。许多新的流重载实际上令人困惑,并且最初使用毫无意义的堆分配非常糟糕地实现。
标签: delphi delphi-2010