Чтение онлайн

на главную - закладки

Жанры

О чём не пишут в книгах по Delphi

Григорьев Антон Борисович

Шрифт:
Листинг 3.39. Запись в файл строки отдельно от структуры

type

 TMethod3Record = packed record

Hour: Word;

Minute: Word;

Second: Word;

MSec: Word;

 end;

procedure TForm1.Butrton3Click(Sender: TObject);

var

 Rec: TMethod3Record;

 Stream: TFileStream;

 Msg: string;

 MsgLen: Integer;

begin

 DecodeTime(Now, Rec.Hour, Rec.Minute, Rec.Second, Rec.MSec);

 Msg := Edit1.Text;

 MsgLen := Length(Msg);

 Stream := TFileStream.Create('Method3.stm', fmCreate);

 Stream.WriteBuffer(Rec, SizeOf(Rec));

 Stream.WriteBuffer(MsgLen, SizeOf(MsgLen);

 if MsgLen > 0 then Stream.WriteBuffer(Pointer(Msg)^, MsgLen);

 Stream.Free;

end;
 

В

проекте RecordRead это следующий код (листинг 3.40).

Листинг 3.40. Чтение из файла строки отдельно от структуры

procedure TForm1.Button3Click(Sender: TObject);

var

 Rec: TMethod3Record;

 Stream: TFileStream;

 Msg: string; MsgLen:

 Integer;

begin

 Stream := TFileStream.Create('Method3.stm', fmOpenRead);

 Stream.ReadBuffer(Rec, SizeOf(Rec));

 Stream.ReadBuffer(MsgLen, SizeOf(Integer));

 SetLength(Msg, MsgLen);

 if MsgLen > 0 then Stream.ReadBuffer(Pointer(Msg)^, MsgLen);

 Stream.Free;

 Label1.Caption :=

TimeToStr(EncodeTime(Rec.Hour, Rec.Minute, Rec.Second, Rec.MSec));

 Label2.Caption := Msg;

end;

Наконец-то мы получили код, который безошибочно передает строку, не имея при этом ограничений длины (кроме ограничения на длину

AnsiString
) и не расходуя понапрасну память. Правда, сам код получился сложнее. Во-первых, из записи исключено поле типа
string
, и теперь ее можно без проблем читать и писать в поток. Во-вторых, в поток после нее записывается длина строки. В-третьих, записывается сама строка.

Параметры вызова методов

ReadBuffer
и
WriteBuffer
для чтения/записи строки требуют дополнительного комментария. Метод
WriteBuffer
пишет в поток ту область памяти, которую занимает указанный в качестве первого параметра объект. Если бы мы указали саму переменную
Msg
, то записалась бы та часть памяти, которую занимает эта переменная, т.е. сам указатель. А нам не нужен указатель, нам необходима та область памяти, на которую он указывает, поэтому указатель следует разыменовать с помощью оператора
^
. Но просто взять и применить этот оператор к переменной
Msg
нельзя — с точки зрения синтаксиса она не является указателем. Поэтому приходится сначала приводить ее к указателю (здесь подошел бы любой указатель, не обязательно нетипизированный). То же самое относится и к
ReadBuffer
: чтобы прочитанные данные укладывались не туда, где хранится указатель на строку, а туда, где хранится сама строка, приходится прибегнуть к такой же конструкции. И обратите внимание, что прежде чем читать строку, нужно зарезервировать для нее память с помощью
SetLength
.

Вместо приведения строки к указателю с последующим его разыменованием можно было бы использовать другие конструкции:

Stream.ReadBuffer(Msg[1], MsgLen);

и

Stream.WriteBuffer(Msg[1], MsgLen);

Это

дает требуемый результат и даже более наглядно: действительно, при чтении и записи мы работаем с той областью памяти, которая начинается с первого символа строки, т.е. с той, где хранится сама строка. Но такой способ менее производителен из-за неявного вызова
UniqueString
. В нашем случае мы и так защищены от побочных изменений других строк (при записи строка не меняется, при чтении она и так уникальна — это обеспечивает
SetLength
), поэтому вполне можем обойтись без этой в данном случае излишней опеки со стороны компилятора.

Примечание

Если сделать

MsgLen
не независимой переменной, а полем записи, можно сэкономить на одном вызове
ReadBuffer
и
WriteBuffer
.

Недостатком этого метода является то, что мы вынуждены переделывать под него запись. В нашем примере это не составило проблемы, но в реальных проектах запись обычно предназначена не только для чтения и сохранения в поток, и если взять и выкинуть из нее строки, то все прочие участки кода станут более громоздкими. Поэтому в реальности приходится писать отдельные процедуры, которые сохраняют запись не как единое целое, а по отдельным полям.

Ранее мы говорили о том, что копирование записей, содержащих поля типа

AnsiString
, в рамках одного процесса маскирует проблему, т.к. указатель остается допустимым и даже (какое-то время) правильным. Но сейчас с помощью приведенного в листинге 3.41 кода (пример RecordCopy на компакт-диске) мы увидим, что проблема не исчезает, а просто становится менее заметной.

Листинг 3.41. Побочное изменение переменной после низкоуровневого копирования

type

 TSomeRecord = record

SomeField: Integer;

Str: string;

 end;

procedure TForm1.Button1Click(Sender: TObject);

var

 Rec: TSomeRecord;

 S: string;

 procedure CopyRecord;

 var

LocalRec: TSomeRecord;

 begin

LocalRec.SomeField := 10;

LocalRec.Str := 'Hello!!!';

UniqueString(LocalRec.Str);

Move(LocalRec, Rec, SizeOf(TSomeRecord));

 end;

begin

 CopyRecord;

 S := 'Good bye';

 UniqueString(S);

 Label1.Caption := Rec.Str;

 Pointer(Rec.Str) := nil;

end;

На экране вместо ожидаемого Hello!!! появится Good bye. Это происходит вот почему: процедура

Move
осуществляет простое побайтное копирование одной области памяти в другую, механизм изменения счетчика ссылок при этом не срабатывает. В результате менеджер памяти не будет знать, что после завершения локальной процедуры
CopyRecord
остаются ссылки на строку "Hello!!!". Память, выделенная этой строке, освобождается. Но
Rec.Str
продолжает ссылаться на эту уже освобожденную память. Для строки
S
выделяется свободная память — та самая, где раньше была строка
LocalRec.Str
. А поскольку
Rec.Str
продолжает ссылаться на эту область памяти, поэтому обращение к ней дает строку "Good bye", которая теперь там размещена.

Поделиться:
Популярные книги

Как я строил магическую империю 4

Зубов Константин
4. Как я строил магическую империю
Фантастика:
боевая фантастика
постапокалипсис
аниме
фантастика: прочее
фэнтези
5.00
рейтинг книги
Как я строил магическую империю 4

Безумный Макс. Поручик Империи

Ланцов Михаил Алексеевич
1. Безумный Макс
Фантастика:
героическая фантастика
альтернативная история
7.64
рейтинг книги
Безумный Макс. Поручик Империи

Попаданка 3

Ахминеева Нина
3. Двойная звезда
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Попаданка 3

Муж на сдачу

Зика Натаэль
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Муж на сдачу

Призыватель нулевого ранга. Том 3

Дубов Дмитрий
3. Эпоха Гардара
Фантастика:
попаданцы
аниме
фэнтези
фантастика: прочее
5.00
рейтинг книги
Призыватель нулевого ранга. Том 3

На границе империй. Том 10. Часть 5

INDIGO
23. Фортуна дама переменчивая
Фантастика:
космическая фантастика
попаданцы
5.00
рейтинг книги
На границе империй. Том 10. Часть 5

Адвокат

Константинов Андрей Дмитриевич
1. Бандитский Петербург
Детективы:
боевики
8.00
рейтинг книги
Адвокат

На границе империй. Том 7

INDIGO
7. Фортуна дама переменчивая
Фантастика:
боевая фантастика
космическая фантастика
попаданцы
6.75
рейтинг книги
На границе империй. Том 7

Здравствуй, 1985-й

Иванов Дмитрий
2. Девяностые
Фантастика:
альтернативная история
5.25
рейтинг книги
Здравствуй, 1985-й

О, Путник!

Арбеков Александр Анатольевич
1. Квинтет. Миры
Фантастика:
социально-философская фантастика
5.00
рейтинг книги
О, Путник!

Чужбина

Седой Василий
2. Дворянская кровь
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Чужбина

Бестужев. Служба Государевой Безопасности. Книга четвертая

Измайлов Сергей
4. Граф Бестужев
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Бестужев. Служба Государевой Безопасности. Книга четвертая

Локки 5. Потомок бога

Решетов Евгений Валерьевич
5. Локки
Фантастика:
юмористическое фэнтези
аниме
фэнтези
5.00
рейтинг книги
Локки 5. Потомок бога

На границе империй. Том 10. Часть 4

INDIGO
Вселенная EVE Online
Фантастика:
боевая фантастика
космическая фантастика
попаданцы
5.00
рейтинг книги
На границе империй. Том 10. Часть 4