Delphi 中重用 TMemoryStream 的意外行为

Unexpected behaviour reusing a TMemoryStream in Delphi

我试图从 TMemoryStream 中读取两个不同长度的字符串,但两个流最终的长度相同。因此,例如,如果第一个字符串是 'abcdefghijkl',第二个字符串是 'wxyz',我得到的第二个字符串的值是 'wxyzefghijkl'(我的新字符串的前四个字符('wxyz') 后跟第一个字符串中未被 'wxyz'

替换的剩余字符

我的代码是:-

var
  L : LongInt
  S : string;

...
  msRecInfo.Position := 0;
  msRecInfo.Read(L, SizeOf(L)); // read size of following string ...

  SubStream.Clear;
  SubStream.CopyFrom(msRecInfo, L); // copy next block of data to a second TMemoryStream

  if (L > 0) then S := StreamToString(SubStream);  //convert the stream into a string

  msRecInfo.Read(L, SizeOf(L)); // get size of following string ...
  SubStream.CopyFrom(msRecInfo, L);
  if (L > 0) then S := StreamToString(SubStream);

我已经为此奋斗了几个小时,但没有成功。谁能指出我做错了什么?

您没有在第二次调用 SubStream.CopyFrom() 之前调用 SubStream.Clear()。因此,对 StreamToString(SubStream) 的第一次调用将 SubStream.Position 留在流的末尾,然后随后的 SubStream.CopyFrom() 将更多数据添加到流中,preserving现有数据。然后随后的 StreamToString(SubStream)SubStream.

中读取所有数据

此外,请注意,如果将 L 传递给 SubStream.CopyFrom()L0,它将复制 整个 msRecInfo 流。这是记录在案的行为:

https://docwiki.embarcadero.com/Libraries/en/System.Classes.TStream.CopyFrom

If Count is 0, CopyFrom sets Source position to 0 before reading and then copies the entire contents of Source into the stream. If Count is greater than or less than 0, CopyFrom reads from the current position in Source.

因此,您需要提高 L > 0 检查,例如:

msRecInfo.Read(L, SizeOf(L));
if (L > 0) then
begin
  SubStream.Clear;
  SubStream.CopyFrom(msRecInfo, L);
  S := StreamToString(SubStream);
end
else
  S := '';

我建议将这个逻辑包装到一个可重用的函数中,例如:

var
  L : LongInt;
  S : string;

  function ReadString: string;
  begin
    msRecInfo.Read(L, SizeOf(L)); // read size of following string ...
    if (L > 0) then
    begin
      SubStream.Clear;
      SubStream.CopyFrom(msRecInfo, L); // copy next block of data to a second TMemoryStream
      Result := StreamToString(SubStream);  //convert the stream into a string
    end else
      Result := '';
  end;
begin
  ...
  msRecInfo.Position := 0;
  S := ReadString;
  S := ReadString;
  ...

虽然,如果可行,我建议完全摆脱 SubStream,更新 StreamToString() 以将 L 作为输入参数,这样您就可以阅读 string 直接来自 msRecInfo,例如:

msRecInfo.Read(L, SizeOf(L));
S := StreamToString(msRecInfo, L);

如果可以避免,则不需要第二次 TMemoryStream