Delphi umožňuje různé možnosti pro přístup k souborům (TFileStream, BlockRead, Read atd), ale ještě je možné použít vysoce efektivní přístup přes soubory mapované do paměti (Memory mapped files) za pomoci Windows.
Tento přístup má několik výhod:
- vysoce efektivní přístup k souborům s HW podporou a cache Windows
- jednoduchá práce jako s pamětí
- efektivní nahodilý přístup (základ pro vícevláknové programy)
- přístup k obrovským souborům (do 4G pro 32 bit Delphi) a přitom soubor není zdaleka celý v paměti
Windows pro tyto případy nabízí tyto API funkce:
- CreateFileMapping
- OpenFileMapping
- MapViewOfFile
- MapViewOfFileEx
- UnmapViewOfFile
- FlushViewOfFile
- CloseHandle
To je jen pro úplnost, nemá cenu přímo používat API, když JCL nabízí pěkné zapouzdření přes následující třídy z jednotky JclFileUtils.pas:
- TJclMappedTextReader
- TJclFileMappingStream
- TJclFileMapping
Ukáži jednoduchý příklad, spočítání výskytu jednotlivých byte v zadaném souboru.
program mmaptest;
{$APPTYPE CONSOLE}
uses
SysUtils, JclFileUtils, Windows;
var
oMappedFile: TJclMappedTextReader;
aiCounts: array [0..255] of integer;
i:Integer;
iTime, iEndTime:Cardinal;
begin
for i:= 0 to 255 do
aiCounts[i] := 0;
iTime := GetTickCount;
oMappedFile := TJclMappedTextReader.Create('test.txt');
try
for i:= 0 to oMappedFile.Size - 1 do
inc(aiCounts[ord(oMappedFile.Chars[i])]);
iEndTime := GetTickCount;
finally
FreeAndNil(oMappedFile);
end;
writeln(Format('Cas: %dms ', [iEndTime - iTime]));
for i := 0 to 255 do
begin
if aiCounts[i] = 0 then
continue;
if i < 32 then
write(format('#%d: %d ', [i, aiCounts[i]]))
else
write(format('"%s": %d ', [Chr(i), aiCounts[i]]))
end;
end.
Řádky 29 až do konce vypisují byte s nenulovým výskytem, ale klíčový řádek je 19, kde se využívá property TJclMappedTextReader.Chars. Tato property je implementovaná tak, že vrací byte (přetypovaný na AnsiChar - kompatibilita s Delphi 2009+, pro neunicode Delphi je to Char) z adresy namapovaného souboru. Jak vypadá?
function TJclAnsiMappedTextReader.GetChars(Index: Integer): AnsiChar;
begin
if (Index < 0) or (Index >= Size) then
raise EJclError.CreateRes(@RsFileIndexOutOfRange);
Result := AnsiChar(PByte(FContent + Index)^);
end;
Je vidět, že je to opravdu jen přístup do paměti, tj. opravdu jednoduchá věc.
Ještě jednou opakuji, že výhodou této metody je opravdu jednoduchý přístup a zjednodušení programu (o zrychlení ani nemluvě).