А как насчёт мне ответить? Сломанную совместимость вы хоть дадите исправить? И, улучшать - я буду, только разрешите. Конечно, совместимость это важно и я её оставлю.
Вставьте в мою программу вывод (например, в файл типа Text) и сверите потом его.
Вот модификация программы, запустите ее и получите в директории запуска два файла - бинарник и текстовый. Когда своей прочитаете, должно получиться то, что в текстовом.
Summary
type
t1 = record
f1: real;
f2: integer;
f3: string;
f4: integer;
f5: string
end;
begin
var f: file;
var b: t1;
Assign(f, 'MyFile.bin');
Rewrite(f);
var ft := OpenWrite('Protocol.txt');
loop Random(3, 5) do
begin
b.f1 := Random;
b.f2 := Random(1, 500);
var s := String('');
loop b.f2 do
s += ChrAnsi(Random(32, 255));
b.f3 := s;
b.f4 := Random(1, 500);
s := String('');
loop b.f4 do
s += ChrAnsi(Random(32, 255));
b.f5 := s;
Write(f, b);
Writeln(ft, b)
end;
f.Close;
ft.Close
end.
Нет, по 2 раза на строку, всего 4. Тип file
использует System.IO.BinaryWriter
чтоб сохранить строку, а он, в свою очередь, сохраняет длину строки в 7-битном формате, перед самой строкой, поэтому получается 4 дескриптора, по 2 на строку.
Мне, если честно, до фонаря как работает низкоуровневое обеспечение. Я пишу на уровне Паскаля, сохраняя в переменной то, чего хочу. Считайте, что это дополнительные поля, если они Вам не нужны - типизированный файл может быть любой структуры!
А сколько байт символ занимает?
В разной кодировке - разное. В оперативке обычно записано в юникоде, поэтому по 2. Ну а в файле бинарные файлы паскаля сохраняюсь по 1 байту на символ.
Похоже, что один. А что мешает получить оба файла и посмотреть в бинарнике?
Собственно, речь о том, что запись произвольных данных в файл легко через бинарник моделируется. Через типизированный файл - никогда. Да и зачем, собственно, если проблема решается через бинарный? Как я уже упоминал, есть такой метод Seek, позволяющий стать перед нужной записью. На типизированном файле он реализован очень просто и эффективно. Зная длину L и номер записи n (от 0) мы сдвигаемся на n*L байт от начала файла и читаем L байт в буфер длиной L А как быть, если длина записи гуляет?
Ничего не понял из этого. Какая совместимость сломана?
Там в файле какая-то дичь. Сейчас разбираться буду.
@Sun_Serega пишет о том, что в ТурбоПаскаль дескриптор фактической длины строки занимает 1 байт. В реализации PascalABC.NET пока длина 0-127 байт, все совпадает. А для длины от 128 байт PascalABC.NET почему-то формирует уже двухбайтный дескриптор, записывая длину в семибитной кодировке. Это он называет несовместимостью на уровне структуры файла.
Так и не смог разобраться в том, что генерирует Паскаль, поэтому чистый .NET:
Uses System.IO;
Uses System;
type
T1 = Record
public Name: String;
public Family: String;
public Age: Int32;
public procedure Serialize(S: Stream);
begin
var bw := new BinaryWriter(S);
bw.Write(self.Age);
bw.Write(Int32(self.Name.Length));
for Var i := 1 to Name.Length do
begin
bw.Write(Int16(self.Name.Chars[i]));
end;
bw.Write(Int32(self.Family.Length));
for Var i := 1 to Family.Length do
begin
bw.Write(Int16(self.Family.Chars[i]));
end;
end;
public Class function Deserialize(S: Stream): t1;
begin
var br := new BinaryReader(S);
Result.Age := br.ReadInt32();
var l: Int32 := br.ReadInt32();
for Var i := 0 to l - 1 do
begin
Result.Name += Char(br.ReadInt16());
end;
l := br.ReadInt32();
for Var i := 0 to l - 1 do
begin
Result.Family += Char(br.ReadInt16());
end;
end;
public procedure Print();
begin
Console.WriteLine(self.Age);
Console.WriteLine(self.Family);
Console.WriteLine(self.Name);
end;
End;
begin
var v1: t1;
v1.Age := 25;
v1.Family := 'Пупкин';
v1.Name := 'Вася';
var fs := System.IO.File.Create('MyFile.bin');
v1.Serialize(fs);
v1.Family := 'Иванов';
v1.Age := 20;
v1.Serialize(fs);
fs.Close();
fs := System.IO.File.OpenRead('MyFile.bin');
while fs.Position < fs.Length do
begin
var v2 := t1.Deserialize(fs);
v2.Print();
Console.WriteLine();
end;
fs.Close();
end.
И? Расскажите, как и насколько быстро стать в таком файле на запись номер 153234 ?
Очень, очень долго, если считывать всё. А если попробовать писать в заголовке разметку, то моментально.
Нет. Только если построите дополнительный файл “указателей” на начало каждой записи. Как следствие, у Вас Seek работать будет крайне неэффективно. А это главное, для чего делались типизированные файлы. Без него ни поиска не построить, ни внешней сортировки.
Поэтому я уже третий день, как дятел, долблю, что нельзя реализовать типизированные файлы со строками неограниченной длины. Файлы - можно, но это уже не будут паскалевские типизированные файлы.
Но тут ведь оказалось, что в PascalABC.NET структура файла не та. То есть, как правильно отметил @Sun_Serega, надо лишь поправить несколько строчек - и всё будет безразмерным. Или же наоборот, размерным. Но мне кажется, что эффективнее всего использовать не паскалевские файлы, а потоки из System.IO и средства для работы с ними. Получается и гибче и эффективнее.
Что значит “не та”? С дескрипторами на 1 байт длиннее для полей, с длиной большей 127? Но при этом осталось главное - длина записи фиксированная! Следовательно - работает seek так, как предписано свыше. Кому нужны безразмерные строки в типизированном файле ценой крайне тормозной работы seek ?
А вот и нет. Если дескриптор строки занимает 2 байта - длина всей записи тоже увеличивается на 1. И это то что Я “долблю”, как вы выразились)). Но, кстати, не 3 а 2, в первый день разговор было не много о другом.
В целом - да, @Gleb говорит вообще о другом, более гибкая реализация с прямым использованием System.IO
- это уже не типизированные файлы. А типизированные файлы должны иметь старую структуру, ради совместимости.
Наконец-то стало понятно, о чем Вы! Если записать две записи, в одной фактически заполнить меньше 128 байт, а в другой - больше, то они окажутся разной длины? Вы это сказать хотите?
Да, вот, к примеру:
type
r1 = record
x1: byte;
s: string[128];
x2: byte;
end;
begin
var f:file of r1;
Rewrite(f,'temp.bin');
var a1,a2,a3:r1;
a1.x1 := $FF;
a1.s := 'abc';
a1.x2 := $EE;
a2.x1 := $DD;
a2.s := '*'*128;
a2.x2 := $CC;
a3.x1 := $BB;
a3.s := 'abc';
a3.x2 := $AA;
f.Write(a1,a2,a3);
f.Close;
end.
Я тут добавил ещё поля по 1 байту до и после строк, чтоб видно было что записи друг на друга не налазят. Да и в целом, чтоб лучше было видно края каждой записи.
Тут a1
и a3
занимают 131 байт, а a2
занимает 132 байт.
Я думал что это будет само собой разумеющееся. Если в каком то месте тратится больше памяти чем должно - значит в другом или 2 записи сожрут кусок друг друга, или у них будет разная длинна. И то и то неприемлемо.
А это мы сейчас проверим. Ну, или почти сейчас))) Посмотрю и отпишусь. Тут есть некоторые эти… “сомнения”. У меня есть виртуальная машинка с несколькими видами паскалей допотопных. Я сейчас там погоняю чуток.