【问题标题】:Delphi: Copy FileStream to MemoryStreamDelphi:将 FileStream 复制到 MemoryStream
【发布时间】:2010-06-05 00:03:13
【问题描述】:

我想将 FileStream 的一部分复制到 MemoryStream。

FileStream.Write(Pointer(MemoryStream)^, MemoryStream.Size);
FileStream.Read(Pointer(MemoryStream)^, count);

对吗?它不适合我。

【问题讨论】:

  • 要格式化您的代码,在编辑器中选择它并按 Control-K。

标签: delphi delphi-7


【解决方案1】:

你必须从 FileStream Read() 到一个单独的缓冲区,然后 Write() 到 MemoryStream,即:

var
  Buffer: PByte;

GetMem(Buffer, NumberOfBytes);
try
  FileStream.ReadBuffer(Buffer^, NumberOfBytes);
  MemoryStream.WriteBuffer(Buffer^, NumberOfBytes);
finally
  FreeMem(Buffer);
end;

由于您正在处理两个 TStream 对象,因此使用 TStream.CopyFrom() 方法会更容易,即:

MemoryStream.CopyFrom(FileStream, NumberOfBytes);

【讨论】:

  • 非常感谢!我使用了 CopyFrom,但我认为您的解决方案会给我带来更好的性能。再次感谢。
  • TStream.CopyFrom() 方法在内部使用了类似的 read-into-buffer-than-write-it 方法,但这样做的错误处理和缓冲区管理比我展示的要多。
  • ReadBuffer 应该在要读取的字节数已知且固定时使用,最好使用 Read - 当没有更多字节时,它实际上可以返回比缓冲区大小更少的字节。我会写: BytesRead := FileStream.Read(Buffer^, NumberOfBytes); MemoryStream.Write(Buffer^, BytesRead);
  • 由于 OP 想要复制文件的一部分,大概 OP 确实提前知道要复制多少字节。
  • 只是想帮助任何可能遇到与我相同问题的人。对于非常大的文件(> 1.5GB),使用 Remy 发布的解决方案比使用 MemoryStream.CopyFrom 方法快得多。我花了 2 天时间弄清楚... :(
【解决方案2】:

以下解决方案不使用单独的缓冲区作为已发布的解决方案。相反,它直接写入目标内存流的缓冲区。 这更快,因为另一个解决方案复制了两次,第一次复制到临时缓冲区,最后复制到内存流。

...
try
  MemoryStream.SetSize(NumberOfBytes); // Allocating buffer
  FileStream.ReadBuffer(MemoryStream.Memory^, NumberOfBytes);
finally
  MemoryStream.Free();
...

这是因为 SetSize 还分配了内存流的缓冲区。 见SetSize documentation

使用 SetSize 设置内存流的大小,然后再填充数据。 SetSize 分配内存缓冲区来保存 NewSize 字节 [...]。


我还使用 CopyFrom 测试了该解决方案,但该解决方案在处理大文件时非常缓慢,因为它似乎使用了非常小的缓冲区。

如果文件非常适合直接使用上述方法读取,则可以使用自己的函数将块直接读取到内存流中。为了比 CopyFrom 方法更快,这些块应该更大。以下代码使用灵活的缓冲区,例如256 兆字节。请随意使用它。

var
  ...
  MemoryStreamPointer: Pointer;
  BlockSize: Integer;
  BytesToRead: Integer;
  BytesRead: Integer;
  RemainingBytes: Integer;

begin
  ...
  BlockSize := 256 * 1024 * 1024; // 256 MiB block size

  MemoryStream.SetSize(NumberOfBytes); // Allocating buffer
  MemoryStreamPointer := MemoryStream.Memory;

  RemainingBytes := NumberOfBytes;
  while RemainingBytes > 0 do
  begin
    BytesToRead := min(RemainingBytes, BlockSize);
    BytesRead := FileStream.Read(MemoryStreamPointer^, BytesToRead);
    RemainingBytes := RemainingBytes - BytesRead;
    MemoryStreamPointer := Pointer(NativeInt(MemoryStreamPointer) + BytesRead);
  end;
  ...
end;

请注意以上代码不包含错误处理。 进一步考虑在读取之前将文件流位置设置为 0。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-12-01
    • 2013-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多