我该如何在 Delphi 中使用一个大文件?

3

当我在MemoryStream或FileStream中使用大文件时,会出现“内存不足”的错误。如何解决这个问题?

示例:

procedure button1.clıck(click);
var
  mem:TMemoryStream;
  str:string;
begin
  mem:=Tmemorystream.create;
  mem.loadfromfile('test.txt');----------> there test.txt size 1 gb..
  compressstream(mem);
end;

为什么你需要一次性将整个文件加载到内存中?按块读取并进行处理不是一个选项吗? - fvu
直接从磁盘上的文件压缩,去掉从文件加载和使用compressstream(mem)的步骤! - Warren P
4个回答

12

你的实现非常凌乱。我不确定CompressStream到底做了什么,但如果你想以流的方式处理大文件,你可以简单地使用TFileStream而不是尝试一次性将整个文件读入TMemoryStream以节省内存。

此外,当你完成对TMemoryStream的使用后,你没有释放它,这意味着你将会泄漏大量的内存。(除非CompressStream负责处理,但从代码中并不清楚,使用这种方式的确不是一个好主意。)


7

由于文件太大,无法在32位地址空间中放入一个连续的块中,因此会出现内存不足错误。

将文件分成较小的部分进行读取,并逐个处理。


我按照你说的做了。如果我将文件分割,你认为处理时间会怎样? - Cenk Aybeyaz
我不知道。我不知道你在做什么文件操作。 - David Heffernan
你认为处理时间怎么样,@Cenk?你期望它有多快,实际上又有多快呢? - Rob Kennedy
@Cenk:现在你不处理它,因此任何处理时间都比根本没有处理时间好... - user160694

5
回答标题中的问题,您需要逐个处理文件片段,必要时逐字节处理:绝对不要一次性将整个文件加载到内存中!如何处理取决于您需要对文件执行什么操作;但是,既然我们知道您正在尝试实现Huffman编码器,我将给您一些具体的提示。
Huffman编码器是一个流编码器:字节进入,位出。每个传入数据单元都将替换为其相应的位模式。编码器不需要一次看到整个文件,因为它实际上只在每次处理一个字节。
以下是如何在不全部加载到内存中的情况下进行Huffman压缩文件的方法;当然,实际的Huffman编码器没有显示,因为问题涉及大文件的处理,而不是构建实际的编码器。此代码片段包括缓冲输入和输出,并显示了如何将实际的编码器过程链接到其中。
(注意,在浏览器中编写的代码;如果无法编译,请修复!)
type THuffmanBuffer = array[0..1023] of Byte; // Because I need to pass the array as parameter

procedure DoActualHuffmanEncoding(const EncodeByte:Byte; var BitBuffer: THuffmanBuffer; var AtBit: Integer);
begin
  // This is where the actual Huffman encoding would happen. This procedure will
  // copy the correct encoding for EncodeByte in BitBuffer starting at AtBit bit index
  // The procedure is expected to advance the AtBit counter with the number of bits
  // that were actually written (that's why AtBit is a var parameter).   
end;

procedure HuffmanEncoder(const FileNameIn, FileNameOut: string);
var InFile, OutFile: TFileStream;
    InBuffer, OutBuffer: THuffmanBuffer;
    InBytesCount: Integer;
    OutBitPos: Integer;
    i: Integer;
begin
  // First open the InFile
  InFile := TFileStream.Create(FileNameIn, fmOpenRead or fmShareDenyWrite);
  try
    // Now prepare the OutFile
    OutFile := TFileStream.Create(FileNameOut, fmCreate);
    try
      // Start the out bit counter
      OutBitPos := 0;
      // Read from the input file, one buffer at a time (for efficiency)
      InBytesCount := InFile.Read(InBuffer, SizeOf(InBuffer));
      while InBytesCount <> 0 do
      begin
        // Process the input buffer byte-by-byte
        for i:=0 to InBytesCount-1 do
        begin
          DoActualHuffmanEncoding(InBuffer[i], OutBuffer, OutBitPos);
          // The function writes bits to the outer buffer, not full bytes, and the
          // encoding for a rare byte might be significantly longer then 1 byte.
          // Whenever the output buffer approaches it's capacity we'll flush it
          // out to the OutFile
          if (OutBitPos > ((SizeOf(OutBuffer)-10)*8) then
          begin
            // Ok, we've got less then 10 bytes available in the OutBuffer, time to
            // flush!
            OutFile.Write(OutBuffer, OutBitPos div 8);
            // We're now possibly left with one incomplete byte in the buffer.
            // We'll copy that byte to the start of the buffer and continue.
            OutBuffer[0] := OutBuffer[OutBitPos div 8];
            OutBitPos := OutBitPos mod 8;
          end;
        end;
        // Read next chunk
        InBytesCount := InFile.Read(InBuffer, SizeOf(InBuffer));
      end;

      // Flush the remaining of the output buffer. This time we want to flush
      // the final (potentially incomplete) byte as well, because we've got no
      // more input, there'll be no more output.
      OutFile.Write(OutBuffer, (OutBitPos + 7) div 8);

    finally OutFile.Free;
    end;     
  finally InFile.Free;
  end;
end;

Huffman 编码器并不难实现,但要做到正确和快速可能会是个挑战。我建议你从实现一个正确的编码器开始,一旦你已经完成了编码和解码的工作,再考虑如何实现一个快速的编码器。


1

你不需要使用内存映射文件来解决这个问题。请解释一下为什么它们可以消除这个问题。 - David Heffernan
这不是唯一的答案,但它是一个可能性,取决于他需要用它做什么。我使用内存映射文件来搜索文件中的字符串/数据,比我加载块等或使用许多其他方法要快得多。 - BugFinder
文件映射(假设有保护页)是将数据输入到[未知]编码器的最佳方法,但链接相当无用,具有奇怪的方法和没有源代码。@Cenk Aybeyaz,您应该真正开始发布相关的代码摘录,并附上您的问题。 - Premature Optimization

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接