在delphi中从文件快速读/写

时间:2009-01-18 19:57:13

标签: delphi file binary

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

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;

6 个答案:

答案 0 :(得分:16)

如果您真的想快速阅读二进制文件,请让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,本文还包含了如何将其用于更多“高级”二进制文件的示例。

答案 1 :(得分:15)

您通常不应该逐字节读取文件。使用较大值的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。

答案 2 :(得分:5)

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

例如:

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;

答案 3 :(得分:3)

如果它是一个足够长的文件,以这种方式读取它会花费相当多的时间,我会使用流来代替。块读取速度会快得多,并且不需要担心循环。像这样:

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);

或类似的东西。

答案 4 :(得分:0)

寻找缓冲的TStream后代。由于磁盘读取速度很快,它会使您的代码更快,但您可以轻松地遍历缓冲区。有各种各样的,或者你可以写自己的。

答案 5 :(得分:0)

如果你感觉很笨,你可以完全绕过Win32并调用NT Native API函数ZwOpenFile(),这在我的非正式测试中确实削减了一点点。否则,我会使用Davy的Memory Mapped File解决方案。