Основы программирования в Linux - Нейл Мэтью
Шрифт:
Интервал:
Закладка:
Функция dbm_error просто проверяет базу данных на наличие ошибок, возвращая 0 при их отсутствии.
dbm_clearerrФункция dbm_clearerr очищает любой флаг ошибочной ситуации, который может быть установлен в базе данных.
dbm_firstkey и dbm_nextkeyЭти подпрограммы обычно используются вместе для просмотра ключей всех элементов базы данных. Далее приведена структура требуемого для просмотра цикла:
DBM *db_ptr;
datum key;
for (key = dbm_firstkey(db_ptr); key.dptr; key = dbm_nextkey(db_ptr));
Выполните упражнение 7.13.
Упражнение 7.13. Извлечение и удалениеВ этом примере вы улучшите файл dbm1.с с помощью описанных новых функций и создадите новый файл dbm2.c.
1. Сделайте копию dbm1.с и откройте его для редактирования. Отредактируйте строку #define TEST_DB_FILE.
#unclude <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <ndbm.h>
#include <string.h>
#define TEST_DB_FILE "/tmp/dbm2_test"
#define ITEMS_USED 3
2. Теперь вам нужно внести изменения только в секцию извлечения:
/* теперь попытайтесь удалить некоторые данные */
sprintf(key_to_use, "bu%d", 13);
key_datum.dptr = key_to_use;
key_datum.dsize = strlen(key_to_use);
if (dbm_delete(dbm_ptr, key_datum) == 0) {
printf("Data with key %s deletedn", key_to_use);
} else {
printf("Nothing deleted for key %sn", key_to_use);
}
for (key_datum = dbm_firstkey(dbm_ptr);
key_datum.dptr;
key_datum = dbm_nextkey(dbm_ptr)) {
data_datum = dbm_fetch(dbm_ptr, key_datum);
if (data_datum.dptr) {
printf("Data retrievedn");
memcpy(&item_retrieved, data_datum.dptr, data_datum.dsize);
printf("Retrieved item - %s %d %sn",
item_retrieved.misc_chars, item_retrieved.any_integer,
item_retrieved.more_chars);
} else {
printf("No data found for key %sn", key_to_use);
}
}
}
Далее приведен вывод:
$ ./dbm2
Data with key bu13 deleted
Data retrieved
Retrieved item — Third 3 baz
Data retrieved
Retrieved item - First! 47 foo
Как это работает
Первая часть программы, идентичная предыдущему примеру, просто сохраняет данные в базе данных. Затем вы формируете ключ, соответствующий второму элементу, и удаляете этот элемент из базы данных.
Далее программа применяет функции dbm_firstkey и dbm_nextkey для обращения к каждому значению ключа по очереди и для извлечения соответствующих ключу данных. Обратите внимание на то, что данные извлекаются не по порядку. Перебор ключей по очереди не определяет никакого порядка извлечения данных, это просто способ просмотра всех элементов в базе данных;
Приложение для работы с коллекцией компакт-дисков
Теперь, когда вы узнали об окружении и управлении данными, самое время обновить приложение. Кажется, что база данных dbm хорошо подходит для хранения информации о компакт-дисках, поэтому воспользуйтесь ею как основой новой реализации.
Обновление проектного решения
Поскольку обновление предполагает значительную корректировку, сейчас самое время взглянуть на проектные решения, чтобы выяснить, не нуждаются ли они в пересмотре. Использование файлов с запятыми в качестве разделителей полей для хранения информации, хотя и обеспечивает легкую реализацию средствами командной оболочки, оказывается связанным со многими ограничениями. Во множестве заголовков компакт-дисков и дорожек встречаются запятые. Вы можете полностью отказаться от этого метода разделения, применив dbm, таким образом, у нас появился один компонент проектного решения, который придется менять.
Разделение информации на заголовки и дорожки с применением отдельного файла для хранения каждого вида данных кажется хорошим решением, поэтому вы можете сохранить эту логическую структуру.
В обеих предыдущих реализациях части приложения, относящиеся к доступу к данным, до некоторой степени смешаны с частями пользовательского интерфейса, нельзя сказать, что незначительно, потому что все приложение реализовано как единый файл. В данной реализации вы примените заголовочный файл для описания данных и подпрограммы для доступа к ним и разделите пользовательский интерфейс и манипулирование данными на два отдельных файла.
Несмотря на то, что вы могли бы сохранить реализацию пользовательского интерфейса средствами библиотеки curses, вы вернетесь к построчной системе. Такой подход позволит сохранить часть приложения, связанную с пользовательским интерфейсом, маленькой и простой и сосредоточиться на других аспектах реализации.
С базой данных dbm вы не сможете применять язык SQL, но опишите новую базу данных с помощью более формальных терминов, используя терминологию языка SQL. Не волнуйтесь, если вы не знакомы с этим языком, мы поясним все определения, а в главе 8 вы узнаете о нем больше. В программном коде таблица может быть описана следующим образом:
CREATE TABLE cdc_entry (
catalog CHAR(30) PRIMARY KEY REFERENCES cdt_entry(catalog),
title CHAR(70),
type CHAR(30),
artist CHAR(70)
);
CREATE TABLE cdt_entry (
catalog CHAR(30) REFERENCES cdc_entry(catalog),
track_no INTEGER,
track_txt CHAR(70),
PRIMARY KEY(catalog, track_no)
);
Это очень краткое описание сообщает имена и размеры полей. В таблице cdc_entry у каждого элемента есть уникальный столбец каталога catalog. В таблице cdt_entry номер дорожки не может быть нулевым и комбинация столбцов catalog и track_no уникальна. Вы увидите их определение в виде структур typedef struct в следующем разделе программного кода.
Приложение управления базой данных компакт-дисков, использующее dbm
Сейчас заново вы создадите приложение, использующее базу данных dbm для хранения нужной вам информации, в виде файлов cd_data.h, app_ui.c и cd_access.c (упражнения 7.14–7.16).
Вы также перепишите пользовательский интерфейс в виде программы с вводом команд. Позже в этой книге интерфейс базы данных и части пользовательского интерфейса будут пересмотрены, когда будет рассматриваться реализация вашего приложения с помощью различных клиент-серверных механизмов и затем как приложения, к которому можно обращаться по сети, используя Web-обозреватель. Преобразование интерфейса в более простой строковый интерфейс позволит сосредоточиться не на нем, а на более важных частях приложения.
ПримечаниеВ последующих главах вы не раз встретитесь с заголовочным файлом базы данных cd_data.h и функциями из файла cd_access.c. Помните о том, что некоторые дистрибутивы Linux требуют немного отличающихся формирующих опций, например, применения в вашем файле на языке С заголовочного файла gdbm-ndbm.h вместо файла ndbm.h и опций -lgdbm_compat -lgdbm вместо просто опции -lgdbm. Если в вашем дистрибутиве Linux дело обстоит именно так, вы должны внести соответствующие изменения в файлы access.с и Makefile.
Упражнение 7.14. Файл cd_data.hНачните с заголовочного файла для определения структуры ваших данных и подпрограмм, которые будут использоваться для доступа к данным.
1. Далее приведено определение структуры данных для базы данных компакт-дисков. В него включено определение структур и размеров двух таблиц, формирующих базу данных. Начните с задания размеров некоторых полей, которые вы будете применять, и двух структур: одной для элемента каталога, а другой для элемента дорожки.
/* Таблица catalog */
#define CAT_CAT_LEN 30
#define CAT_TITLE_LEN 70
#define CAT_TYPE_LEN 30
#define CAT_ARTIST_LEN 70
typedef struct {
char catalog[CAT_CAT_LEN + 1];
char title[CAT_TITLE_LEN + 1];
char type [CAT_TYPE_LEN + 1];
char artist[CAT_ARTIST_LEN + 1];
} cdc_entry;
/* Таблица дорожек, по одному элементу на дорожку */
#define TRACK_CAT_LEN CAT_CAT_LEN
#define TRACK_TTEXT_LEN 70
typedef struct {
char catalog[TRACK_CAT_LEN + 1];
int track_no;
char track_txt[TRACK_TTEXT_LEN + 1];
} cdt_entry;
2. Теперь, имея структуры данных, можно определить нужные вам подпрограммы доступа. Функции с префиксом cdc_ в имени предназначены для элементов каталога, с префиксом cdt_ — для элементов-дорожек.
ПримечаниеУчтите, что некоторые функции возвращают структуры данных. Вы можете указать на аварийное завершение таких функций, сделав содержимое структур пустым.
/* Функции инициализации и завершения */