<=
Основной упор данной страницы - сравнение "file of T" и "BlockFileOf<T>".
Но она также подходит и для обучения основам "BlockFileOf<T>".
Объявление
"file of T":
begin
var f: file of T;
Assign(f, 'temp.bin');
end.
"BlockFileOf<T>":
uses BlockFileOfT;
begin
var f: BlockFileOf<T>;
f := new BlockFileOf<T>; // В отличии от file of T - BlockFileOf<T> нужно всегда инициализировать
f.Assign('temp.bin');
end.
Последнее можно сократить:
uses BlockFileOfT;
begin
var f := new BlockFileOf<T>;
f.Assign('temp.bin');
end.
Или ещё больше сократить:
uses BlockFileOfT;
begin
var f := new BlockFileOf<byte>('temp.bin');
end.
Основы записи/чтения
"file of T":
begin
var f: file of integer;
Rewrite(f, 'temp.bin');
f.Write(
1,2,3,4,5
);
f.Close;
Reset(f);
loop 5 do
f.Read.Print;
f.Close;
end.
"BlockFileOf<T>":
uses BlockFileOfT;
begin
var f := new BlockFileOf<integer>('temp.bin');
f.Rewrite;
f.Write(
1,2,3,4,5
);
f.Close;
f.Reset;
f.Read(5).Print;
f.Close;
end.
В случае с "BlockFileOf<T>" все 5 элементов читаются одним блоком памяти.
В то время как "file of T" читает по одному элементу.
Перечисление всех элементов в файле
"file of T":
begin
var f: file of integer;
Rewrite(f, 'temp.bin');
f.Write(
1,2,3,4,5
);
f.Close;
Reset(f);
f.ReadElements.Print
f.Close;
end.
"BlockFileOf<T>":
uses BlockFileOfT;
begin
var f := new BlockFileOf<integer>('temp.bin');
f.Rewrite;
f.Write(
1,2,3,4,5
);
f.Close;
f.Reset;
f.ToSeq.Print;
f.Close;
end.
"BlockFileOf<T>.ToSeq" работает так же, как и "(file of T).ReadElements" (если не считать более продвинутой защиты от дурака):
Они оба читают файл по одному элементу и после каждого из них проверяют, не достигнут ли конец файла.
Но в "BlockFileOf<T>" есть альтернатива - "ToSeqBlocks".
Эта функция читает всё из файла блоками заданной длины в байтах. Если не задавать длину, данные будут читаться блоками по 4 килобайта.
В нашем случае, "integer" занимает 4 байта в памяти, значит в каждом блоке по 4 КБ поместится 1024 элемента типа "integer"
(Объём типа T можно узнать из свойства "BlockFileOf<T>.TSize", или с помощью sizeof(T) )
В случае с пятью элементами это едва ли даст преимущество, но если записывать и считывать сразу большое количество элементов - считывание блоками будет намного быстрее:
uses BlockFileOfT;
begin
var f := new BlockFileOf<integer>('temp.bin');
f.Rewrite;
f.Write(
1,2,3,4,5
);
f.Close;
f.Reset;
var blocks := f.ToSeqBlocks;
var AsSeq := blocks.SelectMany(bl->bl);
AsSeq.Print;
f.Close;
end.
"AsSeq := blocks.SelectMany(bl->bl)" здесь делает так, что хоть всё считывается из файла блоками,
переменную "AsSeq" будет воспринимать как последовательность элементов, а не последовательность блоков.
И - я расписал вывод как 3 строчки чтоб было виднее. Можно и сократить:
f.Reset;
f.ToSeqBlocks.SelectMany(bl->bl).Print;
f.Close;
Основное преимущество: сохранение записей (record)
uses BlockFileOfT;
type
r1 = record
b1: byte;
i: integer;
b2: byte;
constructor(b1: byte; i: integer; b2: byte);
begin
self.b1 := b1;
self.i := i;
self.b2 := b2;
end;
///Эта функция определяет то, как данную запись выведет на экран
function ToString: string; override :=
$'r1(b1={b1}, i={i}, b2={b2})';
end;
begin
var f := new BlockFileOf<r1>('temp.bin');
f.Rewrite;
f.Write(
new r1(1,2,3)
);
f.Close;
f.Reset;
writeln(f.Read);
f.Close;
end.
Казалось бы, тот же самый код будет работать и для "file of T".
Но вся разница находится в реализации:
"file of T" в данном случае при вызове "Read" сначала прочитал бы названия и типы полей "r1" (через "System.Reflection"),
а затем, по очереди, читал бы из файла значение того же типа, что и у поля ("byte", затем "integer" и снова "byte"),
сохраняя прочитаные значения в соответствующих полях результата.
А "BlockFileOf<T>" подходит к этому с другой стороны.
Когда вы первый раз инициализируете переменную типа "BlockFileOf<r1>" -
объём в памяти, занимаемый переменными типа "r1", вычисляется и сохраняется во внутреннее статическое поле
(Его можно получить через свойство "TSize")
И когда вызывается функция "Read" - "BlockFileOf<T>" читает из файла столько байт, сколько занимают переменные типа "r1",
А потом копирует содержимое полученного массива байт в результат.
То есть, "file of T" читает три блока, а "BlockFileOf<T>" только один.
И когда объём данных одинаковый - количество блоков очень важный фактор.
Поэтому "BlockFileOf<T>" будет быстрее.
И размер одного блока не ограничивается размером одной переменной типа "r1".
"Read(10)", к примеру, прочитает одним блоком то, что "file of T" читал бы как 30 блоков.
Ну и, конечно, всё то же самое касается и "Write".