Delphi での大きなファイルのメモリ マッピングの使用
大きなファイルのメモリマッピングを使用することはほとんどありませんが、そのような要件に遭遇したため、そのプロセスを記録しました。アプリケーションは複雑ではないため、交換を歓迎します。
一部の小さなファイルの場合は、通常のファイル ストリームを使用して問題を解決できますが、2G 以上などの非常に大きなファイルの場合は、ファイル ストリームが機能しないため、API のメモリ マッピング関連のメソッドを使用する必要があります。メモリ マッピングの場合は、ファイル サイズ全体を一度にマッピングすることはできないため、マッピングは一度に小さな部分を処理しながら、チャンクに分けて実行する必要があります。
まずはいくつかの機能を見てみましょう
CreateFile: ファイルを開きます
GetFileSize: ファイルサイズを取得する
CreateFileMapping: マッピングの作成
MapViewOfFile: マッピング ファイル
MapViewOfFile のヘルプを見ると、最後の 2 つのパラメータはページ粒度の整数倍である必要があります。一般に、マシンのページ粒度は 64k (65536 バイト) です。ただし、実際の操作では、これは一般に当てはまりません。 . 任意の位置、任意の長さは可能であるため、何らかの処理を行う必要があります。
この例のタスクは、長さリスト (FInfoList) から長さの値を順番に読み取り、次に別の大きなファイル (FSourceFileName) から指定された長さのデータを順番に読み取ることです。ファイル ストリームを 1 回読み取ってから、順番に読み込むだけで、必要なデータを取得するためにマッピング位置を常に変更する必要があります。
この例では、まず GetSystemInfo でページ粒度を取得し、その 10 倍のページ粒度をマップされたデータ ブロックとして使用し、読み込んだ長さ (totallen) と読み込む長さを足した値を判定します。この時間内にありますマッピング範囲内 (ページ粒度の 10 倍) である場合は読み取りを続行し、それを超えた場合は、残りのデータを記録し、メモリの次のブロックを再マッピングし、記録された残りのデータをマージする必要があります。取得されたデータは少し複雑です (私の考えが複雑すぎる可能性があります)。
コードを以下に示します。
プロシージャ TGetDataThread.DoGetData; var FFile_Handle:THandle; p:PChar; 開始オフセット := 0; TMemoryStream.Create リスト := TStringList.Create; // システム情報を取得 GetSystemInfo(sysinfo); // ページ割り当て粒度 blocksize := sysinfo.dwAllocationGranularity // ファイルを開く FFile_Handle := CreateFile(PChar(FSourceFileName),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0); Fファイルハンドル = INVALID_HANDLE_VALUE then Exit; // ファイル サイズを取得 filesize := GetFileSize(FFile_Handle,nil) // マッピングを作成 FFile_Map := CreateFileMapping(FFile_Handle,nil,PAGE_READONLY,0,0,nil); FFile_Map = 0 の場合、Exit; //ここでは、ブロックサイズの 10 倍を 1 つのデータ ブロックにマップしています。ファイル サイズがブロックサイズの 10 倍未満の場合、filesize div blocksize > 10 then readlen := 10*blocksize else readlen := であれば、ファイル全体の長さが直接マップされます。ファイルサイズ; for i := 0 ~ FInfoList.Count - 1 do begin list.Delimiter := ':'; list.DelimitedText := FInfoList.Strings[i]; //長さを取得します。ここで分析を行いました。保存した情報のタイプは a:b:c であるため、len は ::= StrToInt(list.Strings[1]); で区切られています。 .Strings[2]); if (i = 0) または (totallen+len >=readlen) から開始します。 //読み取り長と読み取り対象の長さがブロックサイズの 10 倍を超える場合、新しいマッピングの内容とマージするために、前のマッピングの最後に内容を保持する必要があります。 if i > 0 then begin offset := offset + readlen ; //一時ストリームに書き込みます tstream.Write(p^,readlen-totallen); //未読のデータの長さが 1 回の割り当てに足りない場合粒度を指定し、残りの長さを直接マップします if filesize-offset < blocksize then readlen := filesize-offset; //マッピング、p はマッピング領域へのポインタです。 //ここの 3 番目のパラメータは常に 0 に設定されることに注意してください。この値は実際の状況に応じて設定する必要があります。 p:= PChar(MapViewOfFile( FFile_Map,FILE_MAP_READ, 0,offset,readlen)) end; //一時ストリームにデータがある場合、tstream.Size > 0 の場合はマージする必要があります。 //一時的なストリーム データを stream.CopyFrom(tstream,tstream.Size) にコピーします //最後に新しいデータを書き込み、完成させます stream.Write(p^,len-tstream.Size); - tstream.Size; //次のデータの先頭を指すようにポインタの位置を移動します。合計 := 合計 + len; Inc(p,len); stream.Position := 0; 最後にストリームをファイルに保存します。フリー ; tstream.Free クローズハンドル(FFile_Map);
ご質問がございましたら、メッセージを残すか、このサイトのコミュニティにアクセスしてご連絡ください。お読みいただきありがとうございます。このサイトをご支援いただきありがとうございます。