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('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; // f2.TSize это то же самое, что sizeof(r2) // У меня 16, хотя sizeof(byte)=1 + sizeof(int64)=8, то есть должно быть 9? // Это потому, что на многих процессорах добавляет отступы (так легче читать/записывать значения) в некоторые записи, так что будьте осторожны // Это также хорошая причина не пользоваться бездумно BaseStream - потому что на разных компьютерах один и тот же код может работать по-разному Writeln(f2.TSize); var f3 := new BlockFileOf; // А вот теперь размер 9, потому что мы явно указали что отступ нам не нужен // Конечно, теперь чтение/запись i может быть медленнее на некоторых процессорах, отступ всё же не для красоты был Writeln(f3.TSize); end.