Быстрое чтение/запись из файла в Делфи

я загружаю файл в массив в двоичной форме, это занимает некоторое время есть ли лучший, более быстрый и эффективный способ сделать это. я использую аналогичный метод для записи обратно в файл.

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 ответов


обычно вы не должны читать файлы байт за байтом. Используйте 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, если файл не удалось открыть или прочитать.


Если вы действительно хотите быстро прочитать двоичный файл, пусть windows беспокоится о буферизации ; -) с помощью Сопоставленные С Памятью Файлы. Используя это, вы можете просто сопоставить файл с местоположением памяти и прочитать его как массив.

ваша функция станет:

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 из моей статьи Быстрое чтение файлов с помощью карты памяти, эта статья также содержит пример того, как использовать его для более "продвинутых" двоичных файлов.


Это зависит от формата файла. Если он состоит из нескольких одинаковых записей, вы можете решить создать файл этого типа записи.

например:

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

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

или что-то подобное.


Поиск буферизованного потомка TStream. Это сделает ваш код намного быстрее, так как чтение диска выполняется быстро, но вы можете легко выполнить цикл через буфер. Есть разные О, или вы можете написать свой собственный.


Если вы чувствуете себя очень растерянным, вы можете полностью обойти Win32 и вызвать функцию NT Native API ZwOpenFile (), которая в моем неофициальном тестировании немного сбривается. В противном случае, я бы использовал решение сопоставленного файла памяти Дэви выше.