【问题标题】:TFileStream read huge files piece by pieceTFileStream 逐个读取大文件
【发布时间】:2013-11-21 19:43:10
【问题描述】:

今天早些时候,我在这里提出了一个问题,询问我在计算机中扫描文件的方法是否正确。作为解决方案,我收到了一些提示,我认为的解决方案之一是:“这需要紧急解决!”,是说内存溢出,一旦我完全读取内存中的文件。所以我开始尝试找到一种方法来逐个读取文件,但我得到了一些东西(错误/虚假),我需要一些帮助来弄清楚如何正确地做到这一点。 这个方法现在很简单:

procedure ScanFile(FileName: string);
const
  MAX_SIZE = 100*1024*1024;
var
  i, aux, ReadLimit: integer;
  MyFile: TFileStream;
  Target: AnsiString;
  PlainText: String;
  Buff: array of byte;
  TotalSize: Int64;
begin
  if (POS('.exe', FileName) = 0) and (POS('.dll', FileName) = 0) and
      (POS('.sys', FileName) = 0) then //yeah I know it's not the best way...
    begin
      try
        MyFile:= TFileStream.Create(FileName, fmOpenRead);
      except on E: EFOpenError do
        MyFile:= NIL;
      end;
      if MyFile <> NIL then
      try
        TotalSize:= MyFile.Size;
        while TotalSize > 0 do begin
          ReadLimit:= Min(TotalSize, MAX_SIZE);
          SetLength(Buff, ReadLimit);
          MyFile.ReadBuffer(Buff[0], ReadLimit);
          PlainText:= RemoveNulls(Buff); //this is to transform the array of bytes in string, I posted the code below too...
          for i:= 1 to Length(PlainText) do
            begin //Begin the search..
            end;
          dec(TotalSize, ReadLimit);
         end;
  finally
    MyFile.Free;
  end;
end;

RemoveNulls 的代码是:

function RemoveNulls(const Buff: array of byte): String;
var
  i: integer;
begin
  for i:= 0 to Length(Buff) do
    begin
      if Buff[i] <> 0 then
        Result:= Result + Chr(Ord(Buff[i]));
    end;
end;

好的,到目前为止我遇到的问题是:

1- 每次重复 while 时,我都会消耗更多的内存,而我原本期望只有 MAX_SIZE 变量中描述的 MAX 100MB,对吧?

2- 我创建了一个文件,其中出现了 2 次应过滤的内容,但由于某种未知原因,我得到了大约 10 次重复出现,看起来我正在重复扫描文件。

感谢您的帮助,如果有人已经完成了此类代码,请在此处发布,我不假装重新创建轮子...

【问题讨论】:

  • 参见@DavidHeffernan 的Buffered files (for faster disk access)
  • 那是我不明白的大量代码。我知道它可以解决我的问题,但如果可能的话,我更喜欢做一些简单的事情。感谢您的关注。
  • Re: 2) 和 4) 那是因为您从文件中请求正好 100MiB,而您必须请求 Min(Count, MAX_SIZE)。我建议重写(为了练习简单,暂时不要处理异常)。
  • @FreeConsulting 是的,我现在做到了:while Count &gt; 0 do begin N:= Min(Count, MAX_SIZE); SetLength(Buff, N); MyFile.ReadBuffer(Buff[0], N); 它解决了 2 和 4...谢谢!
  • 请不要在进行过程中修改问题,cmets 中的“2) 和 4)”现在没有多大意义。

标签: delphi


【解决方案1】:

我会说 RemoveNulls 是您的问题。假设您刚刚将 100MB 读取到传递给 RemoveNulls 的字符串中。然后,您将分配长度为 1 的字符串。重新分配到长度 2。然后到长度 3。然后到长度 4。依此类推,一直到长度 100*1024*1024。

这个过程会使你的记忆碎片化,而且速度慢得惊人。当性能很重要时,应避免堆分配。你根本不需要它。读取文件的一部分,并直接在您读取的缓冲区中搜索。

我可以看到您的代码存在各种问题:

  1. 您的文件扩展名检查已损坏,正如我在您之前的问题中所述。
  2. 正如我在上一个问题中所述,您没有正确处理异常。
  3. RemoveNulls 中的 for 循环缓冲区溢出。从 low() 循环到 high()。

无法对搜索代码发表评论,因为问题中不存在该代码。

【讨论】:

  • 大卫,很抱歉回答迟了。是的,我检查过,当我不调用 RemoveNulls 时,程序可以正常工作而不会发生内存泄漏。这可以通过从低到高循环来解决吗?而不是使用长度?您看到的其他 2 个问题我将尝试解决,但我开始尝试最关键的问题。感谢您的帮助。
  • 通过删除它而不是这样做来修复 RemoveNulls 问题。您已经获得了Buff 中的内容。让您的搜索在 Buff 上运行。
  • 是的,我知道你的意思,而且我已经做过类似的事情了。但问题是我正在处理许多文件类型,其中一些文件类型有例如 ABC...其他有 A{NUL}B{NUL}C{NUL}...其他 可以 有类似 A{NUL}{NUL}B{NUL}C... 这就是为什么我试图“删除空值并只让 Chr 值进行搜索”的原因。这就是我使用那个虚假功能的原因。关于这个问题的任何提示?
  • 我认为您不应该删除空值!如果你必须这样做,这就是你要做的。分配一个与缓冲区长度相同的 ansistring。将变量 strIndx 初始化为 0。遍历缓冲区。当您找到一个非空字符时,将其写入字符串:inc(strIndx); str[strIndx] := buff[buffIndx];。完成后,将str 的长度设置为strIndx。这样可以避免所有重复的堆分配。
  • 我当然不是什么都知道。我在这里花费大量时间的原因之一是为了了解更多信息。我是自学的。 10 岁开始。在大学学习纯数学。从事数字代码的编程工作。在工作中学到了其余的东西。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-12-22
  • 1970-01-01
  • 2012-07-20
  • 2020-11-12
相关资源
最近更新 更多