Быстрое чтение / запись из файла в Delphi - PullRequest
8 голосов
/ 18 января 2009

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

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 ]

16 голосов
/ 19 января 2009

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

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

15 голосов
/ 18 января 2009

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

5 голосов
/ 18 января 2009

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

Например:

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 голосов
/ 19 января 2009

Если файл достаточно длинный, чтобы его чтение занимало заметное количество времени, я бы вместо этого использовал поток Чтение блока будет намного быстрее, и вам не придется беспокоиться о циклах. Примерно так:

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

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

0 голосов
/ 19 января 2009

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

0 голосов
/ 19 января 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...