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

на главную

Жанры

О чём не пишут в книгах по 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", которая теперь там размещена.

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

Ведьма

Резник Юлия
Любовные романы:
современные любовные романы
эро литература
8.54
рейтинг книги
Ведьма

Комбинация

Ланцов Михаил Алексеевич
2. Сын Петра
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Комбинация

Неудержимый. Книга XI

Боярский Андрей
11. Неудержимый
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Неудержимый. Книга XI

Большая игра

Ланцов Михаил Алексеевич
4. Иван Московский
Фантастика:
альтернативная история
5.00
рейтинг книги
Большая игра

Хозяйка Междуречья

Алеева Елена
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
Хозяйка Междуречья

Возвышение Меркурия. Книга 3

Кронос Александр
3. Меркурий
Фантастика:
попаданцы
аниме
5.00
рейтинг книги
Возвышение Меркурия. Книга 3

Восьмое правило дворянина

Герда Александр
8. Истинный дворянин
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Восьмое правило дворянина

Идеальный мир для Лекаря 6

Сапфир Олег
6. Лекарь
Фантастика:
фэнтези
юмористическая фантастика
аниме
5.00
рейтинг книги
Идеальный мир для Лекаря 6

Жена моего брата

Рам Янка
1. Черкасовы-Ольховские
Любовные романы:
современные любовные романы
6.25
рейтинг книги
Жена моего брата

Генерал Империи

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

Купеческая дочь замуж не желает

Шах Ольга
Фантастика:
фэнтези
6.89
рейтинг книги
Купеческая дочь замуж не желает

Академия

Сай Ярослав
2. Медорфенов
Фантастика:
юмористическая фантастика
попаданцы
аниме
5.00
рейтинг книги
Академия

Светлая ведьма для Темного ректора

Дари Адриана
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Светлая ведьма для Темного ректора

Хроники разрушителя миров. Книга 8

Ермоленков Алексей
8. Хроники разрушителя миров
Фантастика:
фэнтези
5.00
рейтинг книги
Хроники разрушителя миров. Книга 8