79 lines
4.0 KiB
ObjectPascal
79 lines
4.0 KiB
ObjectPascal
uses BlockFileOfT;
|
||
|
||
uses System.Runtime.InteropServices; // для StructLayout у r3
|
||
|
||
type
|
||
r1 = record
|
||
b1, b2: byte;
|
||
|
||
constructor(b1, b2: byte);
|
||
begin
|
||
self.b1 := b1;
|
||
self.b2 := b2;
|
||
end;
|
||
|
||
///Переопределение того - как объекты типа r1 будет выписывать writeln
|
||
function ToString: string; override :=
|
||
$'r1({b1}, {b2})';
|
||
|
||
end;
|
||
|
||
r2 = record
|
||
b: byte;
|
||
i: int64;
|
||
end;
|
||
|
||
[StructLayout(LayoutKind.&Explicit, Size=9)]
|
||
r3 = record
|
||
[FieldOffset(0)] b: byte;
|
||
// Явно указываем что i будет хранится на следующем байте после b
|
||
// Так можно даже накладывать поля друг на друга
|
||
// Но поэтому также нужно относиться с осторожностью к такой возможности
|
||
// И помнить у какого типа какой размер, чтобы наложение полей не получилось там, где оно не требуется
|
||
[FieldOffset(1)] i: int64;
|
||
end;
|
||
|
||
begin
|
||
var f := new BlockFileOf<r1>('temp.bin');
|
||
f.Rewrite;
|
||
|
||
f.Write(new r1(1, 2));
|
||
f.Write(new r1(3, 4));
|
||
f.Write(new r1(5, 6));
|
||
|
||
f.Pos := 1;
|
||
var str := f.BaseStream;
|
||
var br := new System.IO.BinaryReader(str);
|
||
Writeln(br.ReadByte); // 3 - потому что прочитало 1 байт, когда файловый курсор стоял в начале элемента #1
|
||
|
||
// А вот так делать не следует. Сейчас курсор находится в середине второго элемента
|
||
// Обычно это вызовет неопределённое поведение и заполнит поля полученной записи мусором
|
||
// Но этот случай простой, поэтому точно известно что прочитает половину второй и половину третьей записи и выведет (4,5)
|
||
Writeln(f.Read);
|
||
// Если вы НЕ пытаетесь специально читать мимо элементов - стоит устанавливать позицию в файле (f.Pos := ...) после прямой работы с BaseStream
|
||
|
||
f.Pos := 0;
|
||
f.PosByte += 1; // сдвигаем курсор на один байт (вся запись f.TSize байт, что, в данном случае, 2)
|
||
// br и f всё ещё работают над тем же потоком, потому что мы не закрывали файл
|
||
// А файловый курсор хранится как раз в потоке
|
||
// Поэтому br можно всё ещё использовать и он будет синхронизирован с f.PosByte
|
||
Writeln(br.ReadByte); // 2, потому что второй байт первой записи
|
||
|
||
f.Close;
|
||
|
||
|
||
|
||
|
||
var f2 := new BlockFileOf<r2>;
|
||
// f2.TSize это то же самое, что sizeof(r2)
|
||
// У меня 16, хотя sizeof(byte)=1 + sizeof(int64)=8, то есть должно быть 9?
|
||
// Это потому, что на многих процессорах добавляет отступы (так легче читать/записывать значения) в некоторые записи, так что будьте осторожны
|
||
// Это также хорошая причина не пользоваться бездумно BaseStream - потому что на разных компьютерах один и тот же код может работать по-разному
|
||
Writeln(f2.TSize);
|
||
|
||
var f3 := new BlockFileOf<r3>;
|
||
// А вот теперь размер 9, потому что мы явно указали что отступ нам не нужен
|
||
// Конечно, теперь чтение/запись i может быть медленнее на некоторых процессорах, отступ всё же не для красоты был
|
||
Writeln(f3.TSize);
|
||
|
||
end. |