【问题标题】:Fast read/write from file in delphi在delphi中快速读取/写入文件
【发布时间】:2010-10-02 03:08:57
【问题描述】:

我正在以二进制形式将文件加载到数组中,这似乎需要一段时间 有没有更好更快更有效的方法来做到这一点。 我正在使用类似的方法写回文件。

procedure openfile(fname:string);
var
    myfile: file;
    filesizevalue,i:integer;
begin
  assignfile(myfile,fname);
  filesizevalue:=GetFileSize(fname); //my method
  SetLength(dataarray, filesizevalue);
  i:=0;
  Reset(myFile, 1);
  while not Eof(myFile) do
    begin
      BlockRead(myfile,dataarray[i], 1);
      i:=i+1;
    end;
  CloseFile(myfile);
end;

【问题讨论】:

  • 请重新格式化,这很难阅读

标签: delphi file binary


【解决方案1】:

如果您真的想快速读取二进制文件,请让 Windows 担心缓冲 ;-) 使用 Memory Mapped Files。使用它,您可以简单地将文件映射到内存位置,就像读取数组一样。

你的函数会变成:

procedure openfile(fname:string);
var
    InputFile: TMappedFile;
begin
  InputFile := TMappedFile.Create;
  try
    InputFile.MapFile(fname);
    SetLength(dataarray, InputFile.Size);
    Move(PByteArray(InputFile.Content)[0], Result[0], InputFile.Size);
  finally
    InputFile.Free;
  end;
end;

但我建议不要使用全局变量 dataarray,而是将其作为 var 传递到参数中,或者使用返回结果数组的函数。

procedure ReadBytesFromFile(const AFileName : String; var ADestination : TByteArray);
var
    InputFile : TMappedFile;
begin
  InputFile := TMappedFile.Create;
  try
    InputFile.MapFile(AFileName);
    SetLength(ADestination, InputFile.Size);
    Move(PByteArray(InputFile.Content)[0], ADestination[0], InputFile.Size);
  finally
    InputFile.Free;
  end;
end;

TMappedFile 来自我的文章Fast reading of files using Memory Mapping,本文还包含一个示例,说明如何将其用于更“高级”的二进制文件。

【讨论】:

  • 你测量过性能吗?我想,内存映射文件应该会胜出,但在我开始重写任何代码之前,我想看看一些数字。
【解决方案2】:

您通常不应该逐字节读取文件。使用具有较大值的 BlockRead(通常最好使用 512 或 1024)并使用其返回值来确定读取了多少字节。

如果大小不是太大(并且您对 SetLength 的使用似乎支持这一点),您还可以使用一个 BlockRead 调用一次读取完整的文件。所以,修改你的方法,这将是:

AssignFile(myfile,fname);
filesizevalue := GetFileSize(fname);
Reset(myFile, 1);
SetLength(dataarray, filesizevalue);
BlockRead(myFile, dataarray[0], filesizevalue);
CloseFile(myfile);

也许您也可以将过程更改为名为 OpenAndReadFile 的布尔函数,如果无法打开或读取文件,则返回 false。

【讨论】:

  • 我确定您只是打错字了,但这不适用于 i 替换为 1 和文件大小值
  • 是的,这是一个错字。它应该是文件大小值,还要注意你的数组应该是一个字节数组。
  • 现已修复。似乎它也必须是 dataarray[0]。
【解决方案3】:

这取决于文件格式。如果它包含多个相同的记录,您可以决定创建该记录类型的文件。

例如:

type
  TMyRecord = record
    fieldA: integer;

    ..
  end;
  TMyFile = file of TMyRecord;

  const
    cBufLen = 100 * sizeof(TMyRecord);
  var
    file: TMyFile;
    i : Integer;

  begin
    AssignFile(file, filename);
    Reset(file);
    i := 0;
    try
      while not Eof(file) do begin
        BlockRead(file, dataarray[i], cBufLen);
        Inc(i, cBufLen);
      end;
    finally
      CloseFile(file);
    end;
  end;

【讨论】:

  • 好答案。我认为您在写入文件时需要使用 packed 关键字,以确保您能够轻松地在 delphi 的未来版本或其他语言中检索内容,因为字段的字节对齐可能会有所不同。
【解决方案4】:

如果文件足够长,以这种方式读取它需要相当长的时间,我会改用流。块读取会快很多,而且不用担心循环。像这样的:

procedure openfile(fname:string);
var
    myfile: TFileStream;
    filesizevalue:integer;
begin
  filesizevalue:=GetFileSize(fname); //my method
  SetLength(dataarray, filesizevalue);
  myFile := TFileStream.Create(fname);
  try
    myFile.seek(0, soFromBeginning);
    myFile.ReadBuffer(dataarray[0], filesizevalue);
  finally
     myFile.free;
  end;
end;

从您的代码看来,您的记录大小为 1 字节长。如果不是,则将读取行更改为:

  myFile.ReadBuffer(dataarray[0], filesizevalue * SIZE);

或类似的东西。

【讨论】:

    【解决方案5】:

    寻找缓冲的 TStream 后代。由于磁盘读取速度很快,这将使您的代码更快,但您可以轻松地循环通过缓冲区。有各种各样的,也可以自己写。

    【讨论】:

      【解决方案6】:

      如果你觉得很头疼,你可以完全绕过 Win32 并调用 NT Native API 函数 ZwOpenFile(),在我的非正式测试中它确实减少了一点点。否则,我会使用上面 Davy 的内存映射文件解决方案。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-07-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多