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:
Windows pro tyto případy nabízí tyto API funkce:
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:
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ě).