Введ$ите названия новых книг.
Нажмите [enter] в начале строки, чтобы закончить ввод.
Metric Merriment
Теперь введите имя автора.
Polly Poetica Теперь введите цену.
18.99
Введите название следующей книги.
Deadly Farce
Теперь введите имя автора.
Dudley Forse Теперь введите цену.
15.99
Введите название следующей книги.
[enter]
Каталог ваших книг:
Metric Merriment авторства Polly Poetica: $18.99 Deadly Farce авторства Dudley Forse: $15.99 Программа завершена.
600 глава 14
$ booksave
Текущее содержимое файла book.dat:
Metric Merriment авторства Polly Poetica: $18.99 Deadly Farce авторства Dudley Forse: $15.99 Введите названия новых книг.
The Third Jar
Теперь введите имя автора.
Nellie Nostrum Теперь введите цену.
22.99
Введите название следующей книги.
[enter]
Каталог ваших книг:
Metric Merriment авторства Polly Poetica: $18.99 Deadly Farce авторства Dudley Forse: $15.99 The Third Jar авторства Nellie Nostrum: $22.99 Программа завершена.
$
При следующем запуске программы booksave.с все три книги будут отображены как текущие записи файла book.dat.
Анализ программы
Для начала файл открывается в режиме "а+b". Часть а+ позволяет программе читать весь файл и добавлять данные в конец файла. Часть b — это принятый в ANSI способ для сообщения о том, что программа будет применять двоичный файловый формат. Для систем Unix, которые не принимают частьb, ее можно опустить, поскольку Unix в любом случае имеется только одна форма файлов. Для реализаций С, предшествующих ANSI, может понадобиться найти локальный эквивалент b.
Двоичный режим был выбран из-за того, что функции fread() и fwrite() предназначены для работы с двоичными файлами. Действительно, некоторое содержимое структуры является текстовым, однако член value — нет. Если вы воспользуетесь текстовым редактором для просмотра файла book.dat, то текстовая часть будет отображаться нормально, но числовая часть окажется нечитабельной и может даже стать причиной выдачи предупреждений.
Вызов rewind() обеспечивает установку указателя позиции в файле на начало файла, приводя его в состояние готовности к первому чтению.
Первый цикл while читает одну структуру за раз в массив структур, останавливаясь при заполнении этого массива либо при исчерпании данных в файле. Переменная filecount отслуживает количество прочитанных структур.
Следующий цикл while запрашивает и получает пользовательский ввод. Как и в листинге 14.2, этот цикл прекращается, когда массив заполнен или пользователь нажал клавишу в начале строки. Обратите внимание, что переменная count начинается со значения, которое она получила по окончании предыдущего цикла. Это приводит к добавлению новых записей в конец массива.
Затем в цикле for выводятся данные, полученные из файла и от пользователя. Так как файл был открыт в режиме добавления, новые записи присоединяются к существующему содержимому.
Мы могли бы воспользоваться циклом и добавлять структуры в конец файла по одной за раз. Однако мы решили прибегнуть к способности функции fwrite() записывать более одного блока за раз. Выражение count - filecount дает количество добавленных новых книг, а вызов fwrite() записывает в файл такое количество блоков
Структуры и другие формы данных 601
размером со структуру. Выражение & library [filecount] — это адрес первой новой структуры в массиве, поэтому копирование начинается с этой точки.
Вероятно, рассмотренный пример является простейшим способом записи структур в файл и их извлечения из него, но в нем в нем может понапрасну расходоваться пространство, поскольку также сохраняются и неиспользуемые части структуры.
Размер структуры составляет 2 * 40 * sizeof (char) + sizeof (float), что в нашей системе дает в сумме 84 байта. Ни одна из записей в действительности не требует всего этого пространства. Тем не менее, одинаковый размер всех порций данных упрощает извлечение данных.
Другой подход заключается в применении записей переменных размеров. Для облегчения считывания таких записей из файла каждая запись может начинаться с числового поля, указывающего размер записи. Это немного сложнее того, что мы только что делали. Обычно данный подход предусматривает использование связных структур, которые мы исследуем далее, и динамического выделения памяти, обсуждаемого в главе 16.
Структуры: что дальше?
Прежде чем завершить исследование структур, мы хотели бы упомянуть одно из наиболее важных применений структур — создание новых форм данных. Для решения определенных задач пользователям компьютера необходимы намного более эффективные формы данных, чем простые массивы и структуры, которые были представлены ранее. Формы данных подобного рода получили собственные названия, такие как очереди, двоичные деревья, кучи, хеш-таблицы и графы. Многие формы построены на основе связных структур. Обычно каждая структура содержит один или два элемента данных плюс один или два указателя на другие структуры того же типа. Эти указатели связывают одну структуру с другой и образуют путь, позволяющий выполнить проход по всей совокупности структур. Например, на рис. 14.3 представлена структура двоичного дерева, где каждая индивидуальная структура (или узел) соединена с двумя структурами уровнем ниже.
Читать дальше