8

ファイルをバイナリ形式で配列にロードしていますが、これには時間がかかるようです。これを行うには、より高速で効率的な方法があります。ファイルに書き戻すために同様の方法を使用しています。

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

6 に答える 6

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からのものです。この記事には、より「高度な」バイナリ ファイルに使用する方法の例も含まれています。

于 2009-01-19T08:17:38.727 に答える
15

通常、ファイルをバイト単位で読み取るべきではありません。大きな値 (多くの場合 512 または 1024 が最適) で BlockRead を使用し、その戻り値を使用して読み取られたバイト数を調べます。

サイズが大きすぎない場合 (そして SetLength の使用がこれをサポートしているように見える場合)、一度に完全なファイルを読み取る 1 つの BlockRead 呼び出しを使用することもできます。したがって、アプローチを変更すると、次のようになります。

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

おそらく、プロシージャを OpenAndReadFile という名前のブール関数に変更し、ファイルを開いたり読み取ったりできない場合は false を返すこともできます。

于 2009-01-18T20:03:02.810 に答える
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;
于 2009-01-18T20:38:26.610 に答える
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);

または似たようなもの。

于 2009-01-19T05:49:39.040 に答える
0

非常に頭がおかしいと感じている場合は、Win32を完全にバイパスして、NTネイティブAPI関数ZwOpenFile()を呼び出すことができます。これは、私の非公式のテストでは少し削り取られています。それ以外の場合は、上記のDavyのメモリマップトファイルソリューションを使用します。

于 2009-01-19T16:41:34.557 に答える
0

バッファリングされた TStream の子孫を探します。ディスクの読み取りが高速に行われるため、コードがはるかに高速になりますが、バッファを簡単にループできます。いろいろありますが、自分で書いてもいいです。

于 2009-01-19T09:32:23.473 に答える