数据管理(八)--CD程序
                                                            生活随笔
收集整理的這篇文章主要介紹了
                                数据管理(八)--CD程序
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.                        
                                
                            
                            
                            CD程序
我們已經了解了環境以及管理數據的相關內容了,現在是更新程序的時候了。dbm數據庫看起來對于存儲我們的CD信息是十分合適的,所以我們會將dbm數據用作我們的新實現的基礎。
更新設計
因為這次更新涉及到一個重要的代碼重寫,所以現在我們需要看一下我們的設計描述以確定是否需要修改。使用以逗號分隔的可以變化的文件來存儲信息,盡管在Shell中很容易實現,但是已經證明是十分嚴格的了。大量的CD標題以及音軌信息在其中需要大量的逗號。如果我們使用dbm就可以放棄這種分隔方法,所以這是我們的設計需要修改的一個元素。
使用單獨的文件來分離標題與音軌之間的信息,看起來是一個好主意,所以我們也會使用這個邏輯安排。
前面的實現看起來在某種程序上將程序的訪問部分與用戶接口部分相混合,至少是因為他們都在一個單獨的文件中實現。在這個實現中,我們使用一個頭文件來描述數據訪問所需要的數據以及方法,并且將用戶接口與數據操作實現在另一個單獨的文件中。
盡管我們可以繼續保持用戶接口的curses實現,但是我們會回歸到一個簡單的基于命令行的系統。這會使得程序的用戶接口部分更為矮小和簡單,并且允許我們關注于其他的實現部分。
盡管我們在dbm代碼中不能使用SQL,但是我們可以使用SQL術語以及更為通常的方式來表達我們的新數據庫。如果我們不熟悉SQL也不要擔心;我們會解釋這些定義。我們將會在第八章了解更多有關SQL的內容。在代碼中,數據表可以用下面的方法來描述:
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表,他告訴我們對于每一個實現記錄都有一個唯一的類別。對于cdc_entry表,他告訴我們音軌信息號不可以為0,而且catalog與track_no的組合是唯一的。
使用dbm的CD數據庫程序
我們現在將要使用dbm數據來實現我們的程序從而存儲我們需要的信息,使用的文件為cd_data.h,app_ui.c以及cd_access.c。
我們同時要將我們的用戶界面重寫為一個命令行程序。在這本書的后面部分,當我們探討使用不同的客戶/服務器機制來實現在我們的程序,以及最后使用Web瀏覽器跨越網絡來訪問程序時,會重用這個程序的數據庫接口以及部分用戶接口。將接口轉換為一個簡單的命令行驅動的接口可以很容易的使得我們的關注于程序的重要部分,而不是接口。
下面我們將要探討的是頭文件cd_data.h以及在后面的章節中cd_access.c中幾次重用的函數。
試驗--cd_data.h
我們會由頭文件開始,定義我們數據的結構,以及我們將會用于訪問數據的函數。
1 這是為CD數據庫而定義的數據結構。他定義了組成數據庫的兩個數據表的結構以及尺寸。我們將會由定義我們將會用到的數據域的尺寸以及兩個結構開始:一個結構用戶類別記錄,而另一個用于音軌記錄。
/* The catalog table */
#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;
/* The tracks table, one entry per track */
#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_開始的函數則用于音軌記錄。
/* Initialization and termination functions */
int database_initialize(const int new_database);
void database_close(void);
/* two for simple data retrieval */
cdc_entry get_cdc_entry(const char *cd_catalog_ptr);
cdt_entry get_cdt_entry(const char *cd_catalog_ptr, const int track_no);
?????? /* two for data addition */
?????? int add_cdc_entry(const cdc_entry entry_to_add);
?????? int add_cdt_entry(const cdt_entry entry_to_add);
?????? /* two for data deletion */
?????? int del_cdc_entry(const char *cd_catalog_ptr);
?????? int del_cdt_entry(const char *cd_catalog_ptr, const int track_no);
?????? /* one search function */
?????? cdc_entry search_cdc_entry(const char *cd_catalog_ptr, int *first_call_ptr);
試驗--app_ui.c
現在我們開始探討用戶接口。這會給我們一個相對簡單的程序,通過他可以訪問數據庫函數。我們將會在一個單獨的文件中實現這個接口。
1 如平時一樣,我們由頭文件開始:
#define _XOPEN_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include “cd_data.h”
#define TMP_STRING_LEN 125 /* this number must be larger than the biggest
????????????????????????????? single string in any database structure */
2 我們定義我們的菜單選項。在這里使用#defined常量定義的形式,因為他會允許編譯器檢查菜單選項變量的類型。
typedef enum {
??? mo_invalid,
??? mo_add_cat,
??? mo_add_tracks,
??? mo_del_cat,
??? mo_find_cat,
??? mo_list_cat_tracks,
??? mo_del_tracks,
??? mo_count_entries,
??? mo_exit
} menu_options;
3 現在我們編寫局部函數的原型。要記住,實際訪問數據的原型包含在cd_data.h中。
static int command_mode(int argc, char *argv[]);
static void announce(void);
static menu_options show_menu(const cdc_entry *current_cdc);
static int get_confirm(const char *question);
static int enter_new_cat_entry(cdc_entry *entry_to_update);
static void enter_new_track_entries(const cdc_entry *entry_to_add_to);
static void del_cat_entry(const cdc_entry *entry_to_delete);
static void del_track_entries(const cdc_entry *entry_to_delete);
static cdc_entry find_cat(void);
static void list_tracks(const cdc_entry *entry_to_use);
static void count_all_entries(void);
static void display_cdc(const cdc_entry *cdc_to_show);
static void display_cdt(const cdt_entry *cdt_to_show);
static void strip_return(char *string_to_strip);
4 最后我們進行main函數。這個函數由以保證我們用來保持當前選中的CD類別記錄的音軌信息的current_cdc_entry已經進行了初始化而開始的。同時我們也要分析命令行,從而得到所運行的程序,并且初始化數據庫。
void main(int argc, char *argv[])
{
??? menu_options current_option;
??? cdc_entry current_cdc_entry;
??? int command_result;
??? memset(¤t_cdc_entry, ‘/0’, sizeof(current_cdc_entry));
??? if (argc > 1) {
??????? command_result = command_mode(argc, argv);
??????? exit(command_result);
??? }
??? announce();
??? if (!database_initialize(0)) {
??????? fprintf(stderr, “Sorry, unable to initialize database/n”);
??????? fprintf(stderr, “To create a new database use %s -i/n”, argv[0]);
??????? exit(EXIT_FAILURE);
??? }
5 現在我們已經準備好處理用戶輸入了。我們在一個循環中,提示菜單選項,并且進行處理,直到用戶選擇了退出選項。注意,在這里我們將current_cdc_entry結構傳遞給show_menu函數。我們這樣做就要使得如果當前選中了一個類別時菜單選項可以發生變化。
while(current_option != mo_exit) {
??????? current_option = show_menu(¤t_cdc_entry);
??????? switch(current_option) {
??????????? case mo_add_cat:
??????????????? if (enter_new_cat_entry(¤t_cdc_entry)) {
??????????????????? if (!add_cdc_entry(current_cdc_entry)) {
??????????????????????? fprintf(stderr, “Failed to add new entry/n”);
??????????????????????? memset(¤t_cdc_entry, ‘/0’,
??????????????????????????????? sizeof(current_cdc_entry));
??????????????????? }
??????????????? }
??????????????? break;
??????????? case mo_add_tracks:
??????????? enter_new_track_entries(¤t_cdc_entry);
??????????? break;
??????? case mo_del_cat:
??????????? del_cat_entry(¤t_cdc_entry);
??????????? break;
??????? case mo_find_cat:
??????????? current_cdc_entry = find_cat();
??????????? break;
??????? case mo_list_cat_tracks:
??????????? list_tracks(¤t_cdc_entry);
??????????? break;
??????? case mo_del_tracks:
??????????? del_track_entries(¤t_cdc_entry);
??????????? break;
??????? case mo_count_entries:
??????????? count_all_entries();
??????????? break;
??????? case mo_exit:
??????????? break;
??????? case mo_invalid:
??????????? break;
??????? default:
??????????? break;
??? } /* switch */
} /* while */
6 當main循環退出時,我們關閉數據并且退回到環境。通過announce函數來打印歡迎界面:
??? database_close();
??? exit(EXIT_SUCCESS);
} /* main */
static void announce(void)
{
??? printf(“/n/nWelcome to the demonstration CD catalog database /
???????????? program/n”);
}
7 在這里我們實現了show_menu函數。這個函數會檢測當前類別是否使用類別名的第一個字母被選中。如果一個類別被選中就會出現更多的選項。
static menu_options show_menu(const cdc_entry *cdc_selected)
{
??? char tmp_str[TMP_STRING_LEN + 1];
??? menu_options option_chosen = mo_invalid;
? while (option_chosen == mo_invalid) {
????? if (cdc_selected->catalog[0]) {
????????? printf(“/n/nCurrent entry: “);
????????? printf(“%s, %s, %s, %s/n”, cdc_selected->catalog,
???????????????? cdc_selected->title,
???????????????? cdc_selected->type,
???????????????? cdc_selected->artist);
????????? printf(“/n”);
????????? printf(“1 - add new CD/n”);
????????? printf(“2 - search for a CD/n”);
????????? printf(“3 - count the CDs and tracks in the database/n”);
????????? printf(“4 - re-enter tracks for current CD/n”);
????????? printf(“5 - delete this CD, and all its tracks/n”);
????????? printf(“6 - list tracks for this CD/n”);
????????? printf(“q - quit/n”);
????????? printf(“/nOption: “);
????????? fgets(tmp_str, TMP_STRING_LEN, stdin);
????????? switch(tmp_str[0]) {
????????????? case ‘1’: option_chosen =? mo_add_cat; break;
????????????? case ‘2’: option_chosen =? mo_find_cat; break;
????????????? case ‘3’: option_chosen =? mo_count_entries; break;
????????????? case ‘4’: option_chosen =? mo_add_tracks; break;
????????????? case ‘5’: option_chosen =? mo_del_cat; break;
????????????? case ‘6’: option_chosen =? mo_list_cat_tracks; break;
????????????? case ‘q’: option_chosen =? mo_exit; break;
????????? }
????? }
????? else {
????????? printf(“/n/n”);
????????? printf(“1 - add new CD/n”);
????????? printf(“2 - search for a CD/n”);
????????? printf(“3 - count the CDs and tracks in the database/n”);
????????? printf(“q - quit/n”);
????????? printf(“/nOption: “);
????????? fgets(tmp_str, TMP_STRING_LEN, stdin);
????????? switch(tmp_str[0]) {
????????????? case ‘1’: option_chosen = mo_add_cat; break;
????????????? case ‘2’: option_chosen = mo_find_cat; break;
????????????? case ‘3’: option_chosen = mo_count_entries; break;
????????????? case ‘q’: option_chosen = mo_exit; break;
????????? }
????? }
? } /* while */
? return(option_chosen);
}
8 有多個地方我們希望詢問用戶他是否確定所請求的動作。我們并不是在代碼中的多個地方詢問用戶,相反,我們會將其作為一個單獨的get_confirm函數來實現:
static int get_confirm(const char *question)
{
??? char tmp_str[TMP_STRING_LEN + 1];
? printf(“%s”, question);
? fgets(tmp_str, TMP_STRING_LEN, stdin);
? if (tmp_str[0] == ‘Y’ || tmp_str[0] == ‘y’) {
????? return(1);
? }
? return(0);
}
9 函數enter_new_cat_entry允許用戶可以輸入新的類別記錄。但是并不希望存儲由fgets函數所返回的回車,所以我們要去掉回車。
static int enter_new_cat_entry(cdc_entry *entry_to_update)
{
??? cdc_entry new_entry;
??? char tmp_str[TMP_STRING_LEN + 1];
??? memset(&new_entry, ‘/0’, sizeof(new_entry));
??? printf(“Enter catalog entry: “);
??? (void)fgets(tmp_str, TMP_STRING_LEN, stdin);
??? strip_return(tmp_str);
??? strncpy(new_entry.catalog, tmp_str, CAT_CAT_LEN - 1);
??? printf(“Enter title: “);
??? (void)fgets(tmp_str, TMP_STRING_LEN, stdin);
??? strip_return(tmp_str);
??? strncpy(new_entry.title, tmp_str, CAT_TITLE_LEN - 1);
??? printf(“Enter type: “);
??? (void)fgets(tmp_str, TMP_STRING_LEN, stdin);
??? strip_return(tmp_str);
??? strncpy(new_entry.type, tmp_str, CAT_TYPE_LEN - 1);
??? printf(“Enter artist: “);
??? (void)fgets(tmp_str, TMP_STRING_LEN, stdin);
??? strip_return(tmp_str);
??? strncpy(new_entry.artist, tmp_str, CAT_ARTIST_LEN - 1);
??? printf(“/nNew catalog entry entry is :-/n”);
??? display_cdc(&new_entry);
??? if (get_confirm(“Add this entry ?”)) {
??????? memcpy(entry_to_update, &new_entry, sizeof(new_entry));
??????? return(1);
??? }
??? return(0);
}
10 現在我們要來討論輸入音軌信息的enter_new_track_entries函數。相比類別記錄函數,這個函數要復雜一些,因為我們允許保存已經存在的音軌記錄。
static void enter_new_track_entries(const cdc_entry *entry_to_add_to)
{
??? cdt_entry new_track, existing_track;
??? char tmp_str[TMP_STRING_LEN + 1];
??? int track_no = 1;
??? if (entry_to_add_to->catalog[0] == ‘/0’) return;
??? printf(“/nUpdating tracks for %s/n”, entry_to_add_to->catalog);
??? printf(“Press return to leave existing description unchanged,/n”);
??? printf(“ a single d to delete this and remaining tracks,/n”);
??? printf(“ or new track description/n”);
??? while(1) {
11 首先,我們需要檢測是否存在當前音軌號的音軌記錄。依據我們需要查找的內容,我們可以改變提示符。
memset(&new_track, ‘/0’, sizeof(new_track));
existing_track = get_cdt_entry(entry_to_add_to->catalog,
???????????????????????????????? track_no);
if (existing_track.catalog[0]) {
??? printf(“/tTrack %d: %s/n”, track_no,
?????????????????? existing_track.track_txt);
??? printf(“/tNew text: “);
}
else {
??? printf(“/tTrack %d description: “, track_no);
}
fgets(tmp_str, TMP_STRING_LEN, stdin);
strip_return(tmp_str);
12 如果并不存在所要查找的音軌信息,而用戶并沒有添加音軌信息,我們假設用戶并沒有音軌信息需要添加。
if (strlen(tmp_str) == 0) {
??? if (existing_track.catalog[0] == ‘/0’) {
??????????? /* no existing entry, so finished adding */
??????? break;
??? }
??? else {
??????? /* leave existing entry, jump to next track */
??????? track_no++;
??????? continue;
??? }
}
13 如果用戶輸入了一個d字符,這會刪除當前以及更高記錄號的音軌信息。如果并沒有查找到要刪除的音軌信息,del_cat_entry函數會返回false。
if ((strlen(tmp_str) == 1) && tmp_str[0] == ‘d’) {
??????? /* delete this and remaining tracks */
??? while (del_cdt_entry(entry_to_add_to->catalog, track_no)) {
??????? track_no++;
??? }
??? break;
}
14 現在我們要來編碼添加一個新的音軌信息或是更新一個已經存在的音軌信息。我們使用cdt_entry構成new_track,然后調用數據庫函數add_cdt_entry將其添加到數據庫中。
????? strncpy(new_track.track_txt, tmp_str, TRACK_TTEXT_LEN - 1);
????? strcpy(new_track.catalog, entry_to_add_to->catalog);
????? new_track.track_no = track_no;
????? if (!add_cdt_entry(new_track)) {
????????? fprintf(stderr, “Failed to add new track/n”);
????????? break;
????? }
????? track_no++;
? } /* while */
}
15 函數del_cat_entry刪除一個類別記錄。我們絕不會允許一個不存在類別的音軌記錄存在。
static void del_cat_entry(const cdc_entry *entry_to_delete)
{
??? int track_no = 1;
??? int delete_ok;
??? display_cdc(entry_to_delete);
??? if (get_confirm(“Delete this entry and all it’s tracks? “)) {
??????? do {
??????????? delete_ok = del_cdt_entry(entry_to_delete->catalog,
?????????????????????????????????????? track_no);
??????????? track_no++;
??????? } while(delete_ok);
??????? if (!del_cdc_entry(entry_to_delete->catalog)) {
??????????? fprintf(stderr, “Failed to delete entry/n”);
??????? }
??? }
}
16 下一個函數是刪除一個類別的所有音軌信息的程序:
static void del_track_entries(const cdc_entry *entry_to_delete)
{
??? int track_no = 1;
??? int delete_ok;
??? display_cdc(entry_to_delete);
??? if (get_confirm(“Delete tracks for this entry? “)) {
??????? do {
??????????? delete_ok = del_cdt_entry(entry_to_delete->catalog, track_no);
??????????? track_no++;
??????? } while(delete_ok);
??? }
}
17 下面,我們創建一個非常簡單的類別查找程序。我們允許用戶輸入一個字符串,然后檢測包含這個字符串的類別。因為也許會匹配多個記錄,我們只是簡單的依次用戶提供匹配的記錄:
static cdc_entry find_cat(void)
{
??? cdc_entry item_found;
??? char tmp_str[TMP_STRING_LEN + 1];
??? int first_call = 1;
??? int any_entry_found = 0;
??? int string_ok;
??? int entry_selected = 0;
??? do {
??????? string_ok = 1;
??????? printf(“Enter string to search for in catalog entry: “);
??????? fgets(tmp_str, TMP_STRING_LEN, stdin);
??????? strip_return(tmp_str);
??????? if (strlen(tmp_str) > CAT_CAT_LEN) {
??????????? fprintf(stderr, “Sorry, string too long, maximum %d /
????????????????????????????? characters/n”, CAT_CAT_LEN);
??????????? string_ok = 0;
??????? }
??? } while (!string_ok);
??? while (!entry_selected) {
??????? item_found = search_cdc_entry(tmp_str, &first_call);
??????? if (item_found.catalog[0] != ‘/0’) {
??????????? any_entry_found = 1;
??????????? printf(“/n”);
??????????? display_cdc(&item_found);
??????????? if (get_confirm(“This entry? “)) {
??????????????? entry_selected = 1;
??????????? }
??????? }
??????? else {
??????????? if (any_entry_found) printf(“Sorry, no more matches found/n”);
??????????? else printf(“Sorry, nothing found/n”);
??????????? break;
????? }
? }
? return(item_found);
}
18 list_tracks是一個打印一個指定的類別內的所音軌的函數:
static void list_tracks(const cdc_entry *entry_to_use)
{
??? int track_no = 1;
??? cdt_entry entry_found;
??? display_cdc(entry_to_use);
??? printf(“/nTracks/n”);
??? do {
??????????? entry_found = get_cdt_entry(entry_to_use->catalog,
??????????????????????????????????????? track_no);
??????????? if (entry_found.catalog[0]) {
??????????????? display_cdt(&entry_found);
??????????????? track_no++;
??????????? }
??? } while(entry_found.catalog[0]);
??? (void)get_confirm(“Press return”);
} /* list_tracks */
19 count_all_entries函數統計所有的音軌信息記錄數:
static void count_all_entries(void)
{
??? int cd_entries_found = 0;
??? int track_entries_found = 0;
??? cdc_entry cdc_found;
??? cdt_entry cdt_found;
??? int track_no = 1;
??? int first_time = 1;
??? char *search_string = “”;
??? do {
??????? cdc_found = search_cdc_entry(search_string, &first_time);
??????? if (cdc_found.catalog[0]) {
??????????? cd_entries_found++;
??????????? track_no = 1;
??????????? do {
??????????????? cdt_found = get_cdt_entry(cdc_found.catalog, track_no);
??????????????? if (cdt_found.catalog[0]) {
??????????????????? track_entries_found++;
??????????????????? track_no++;
??????????????? }
??????????? } while (cdt_found.catalog[0]);
??????? }
??? } while (cdc_found.catalog[0]);
??? printf(“Found %d CDs, with a total of %d tracks/n”, cd_entries_found,
??????????????? track_entries_found);
??? (void)get_confirm(“Press return”);
}
20 現在我們編寫display_cdc函數,一個用于顯示一個類別記錄的函數:
static void display_cdc(const cdc_entry *cdc_to_show)
{
??? printf(“Catalog: %s/n”, cdc_to_show->catalog);
??? printf(“/ttitle: %s/n”, cdc_to_show->title);
??? printf(“/ttype: %s/n”, cdc_to_show->type);
??? printf(“/tartist: %s/n”, cdc_to_show->artist);
}
以及用于顯示單一音軌記錄的display_cdt函數:
static void display_cdt(const cdt_entry *cdt_to_show)
{
??? printf(“%d: %s/n”, cdt_to_show->track_no, cdt_to_show->track_txt);
}
21 函數strip_return用于移除字符串末尾的回車符。記住,與Unix類似,在Linux中,使用一個回車來代表一行的結束:
static void strip_return(char *string_to_strip)
{
??? int len;
??? len = strlen(string_to_strip);
??? if (string_to_strip[len - 1] == ‘/n’) string_to_strip[len - 1] = ‘/0’;
}
22 command_mode是一個用于分析命令行參數的函數。getopt函數是一個很好的方法,可以用來保證我們的程序接受符合標準Linux約定的參數。
static int command_mode(int argc, char *argv[])
{
??? int c;
??? int result = EXIT_SUCCESS;
??? char *prog_name = argv[0];
??? /* these externals used by getopt */
??? extern char *optarg;
??? extern optind, opterr, optopt;
??? while ((c = getopt(argc, argv, “:i”)) != -1) {
??????? switch(c) {
??????????? case ‘i’:
??????????????? if (!database_initialize(1)) {
??????????????????? result = EXIT_FAILURE;
??????????????????? fprintf(stderr, “Failed to initialize database/n”);
??????????????? }
??????????????? break;
??????????? case ‘:’:
??????????? case ‘?’:
??????????? default:
????????????? fprintf(stderr, “Usage: %s [-i]/n”, prog_name);
????????????? result = EXIT_FAILURE;
????????????? break;
????? } /* switch */
? } /* while */
? return(result);
}
試驗--cd_access.c
現在我們來討論訪問dbm數據的函數。
1 如平時一樣,我們由#include語句開始。然后我們使用#define語句來指定我們將會用于存儲數據文件。
#define _XOPEN_SOURCE
#include? <unistd.h>
#include? <stdlib.h>
#include? <stdio.h>
#include? <fcntl.h>
#include? <string.h>
#include? <ndbm.h>
#include “cd_data.h”
#define? CDC_FILE_BASE “cdc_data”
#define? CDT_FILE_BASE “cdt_data”
#define? CDC_FILE_DIR “cdc_data.dir”
#define? CDC_FILE_PAG “cdc_data.pag”
#define? CDT_FILE_DIR “cdt_data.dir”
#define? CDT_FILE_PAG “cdt_data.pag”
2 我們使用下面的兩個局部變量來保存當前數據庫的信息:
static DBM *cdc_dbm_ptr = NULL;
static DBM *cdt_dbm_ptr = NULL;
3 默認情況下,database_initialize函數會打開一個已經存在數據庫,但是通過傳遞一個非零的參數new_database,我們可以強制其創建一個新的數據庫,但是移除當前已存在的數據庫。如果數據庫成功的進行初始化,兩個數據庫也會進行初始,來表明已打開了一個數據庫。
int database_initialize(const int new_database)
{
??? int open_mode = O_CREAT | O_RDWR;
??? /* If any existing database is open then close it */
??? if (cdc_dbm_ptr) dbm_close(cdc_dbm_ptr);
??? if (cdt_dbm_ptr) dbm_close(cdt_dbm_ptr);
??? if (new_database) {
??????? /* delete the old files */
????? (void) unlink(CDC_FILE_PAG);
????? (void) unlink(CDC_FILE_DIR);
????? (void) unlink(CDT_FILE_PAG);
????? (void) unlink(CDT_FILE_DIR);
? }
? /* Open some new files, creating them if required */
? cdc_dbm_ptr = dbm_open(CDC_FILE_BASE, open_mode, 0644);
? cdt_dbm_ptr = dbm_open(CDT_FILE_BASE, open_mode, 0644);
? if (!cdc_dbm_ptr || !cdt_dbm_ptr) {
????? fprintf(stderr, “Unable to create database/n”);
????? cdc_dbm_ptr = cdt_dbm_ptr = NULL;
????? return (0);
? }
? return (1);
}
4 database_close函數只是簡單的關閉所打開的數據庫,并且設置兩個數據庫指針指向null來表明當前沒有數據庫打開。
void database_close(void)
{
??? if (cdc_dbm_ptr) dbm_close(cdc_dbm_ptr);
??? if (cdt_dbm_ptr) dbm_close(cdt_dbm_ptr);
??? cdc_dbm_ptr = cdt_dbm_ptr = NULL;
}
5 接下來我們會編寫一個函數,當向這個函數傳遞一個類別字符串時,此函數會取回此類別記錄。如果沒有找到此記錄,返回的數據會有一個空的類別區域。
cdc_entry get_cdc_entry(const char *cd_catalog_ptr)
{
??? cdc_entry entry_to_return;
??? char entry_to_find[CAT_CAT_LEN + 1];
??? datum local_data_datum;
??? datum local_key_datum;
??? memset(&entry_to_return, ‘/0’, sizeof(entry_to_return));
6 我們會在開始時進行一些檢測,來保證數據庫已經打開,并且我們傳遞了合理的參數--也就是說,查找關鍵字只包含可用的字符串以及null。
if (!cdc_dbm_ptr || !cdt_dbm_ptr) return (entry_to_return);
if (!cd_catalog_ptr) return (entry_to_return);
if (strlen(cd_catalog_ptr) >= CAT_CAT_LEN) return (entry_to_return);
memset(&entry_to_find, ‘/0’, sizeof(entry_to_find));
strcpy(entry_to_find, cd_catalog_ptr);
7 我們設置dbm函數所需要的datum結構,然后使用dbm_fetch函數來取出數據。如果沒有取回任何數據,我們就會返回我們先前所初始化的空的entry_to_return結構。
??? local_key_datum.dptr = (void *) entry_to_find;
??? local_key_datum.dsize = sizeof(entry_to_find);
??? memset(&local_data_datum, ‘/0’, sizeof(local_data_datum));
??? local_data_datum = dbm_fetch(cdc_dbm_ptr, local_key_datum);
??? if (local_data_datum.dptr) {
??????? memcpy(&entry_to_return, (char *)local_data_datum.dptr,
?????????????????? local_data_datum.dsize);
??? }
??? return (entry_to_return);
} /* get_cdc_entry */
8 我們最好也可以得到一個單一的音軌信息,與get_cdc_entry函數相類似,而這也正是下一個函數所要做的,但是需要一個指向類別字符的指針以及一個音軌序號作為參數。
cdt_entry get_cdt_entry(const char *cd_catalog_ptr, const int track_no)
{
??? cdt_entry entry_to_return;
??? char entry_to_find[CAT_CAT_LEN + 10];
??? datum local_data_datum;
??? datum local_key_datum;
??? memset(&entry_to_return, ‘/0’, sizeof(entry_to_return));
??? if (!cdc_dbm_ptr || !cdt_dbm_ptr) return (entry_to_return);
??? if (!cd_catalog_ptr) return (entry_to_return);
??? if (strlen(cd_catalog_ptr) >= CAT_CAT_LEN) return (entry_to_return);
??? /* set up the search key, which is a composite key of catalog entry
?????? and track number */
??? memset(&entry_to_find, ‘/0’, sizeof(entry_to_find));
??? sprintf(entry_to_find, “%s %d”, cd_catalog_ptr, track_no);
??? local_key_datum.dptr = (void *) entry_to_find;
??? local_key_datum.dsize = sizeof(entry_to_find);
??? memset(&local_data_datum, ‘/0’, sizeof(local_data_datum));
??? local_data_datum = dbm_fetch(cdt_dbm_ptr, local_key_datum);
??? if (local_data_datum.dptr) {
??????? memcpy(&entry_to_return, (char *) local_data_datum.dptr,
?????????????????? local_data_datum.dsize);
??? }
??? return (entry_to_return);
}
9 下一個函數,add_cdc_entry,添加一個新的類別記錄:
int add_cdc_entry(const cdc_entry entry_to_add)
{
??? char key_to_add[CAT_CAT_LEN + 1];
??? datum local_data_datum;
??? datum local_key_datum;
??? int result;
? /* check database initialized and parameters valid */
? if (!cdc_dbm_ptr || !cdt_dbm_ptr) return (0);
? if (strlen(entry_to_add.catalog) >= CAT_CAT_LEN) return (0);
? /* ensure the search key contains only the valid string and nulls */
? memset(&key_to_add, ‘/0’, sizeof(key_to_add));
? strcpy(key_to_add, entry_to_add.catalog);
? local_key_datum.dptr = (void *) key_to_add;
? local_key_datum.dsize = sizeof(key_to_add);
? local_data_datum.dptr = (void *) &entry_to_add;
? local_data_datum.dsize = sizeof(entry_to_add);
? result = dbm_store(cdc_dbm_ptr, local_key_datum, local_data_datum,
????????????????????? DBM_REPLACE);
? /* dbm_store() uses 0 for success */
? if (result == 0) return (1);
? return (0);
}
10 add_cdt_entry添加一新的音軌記錄。訪問關鍵字是類別字符串,并且音軌序號作為組合。
int add_cdt_entry(const cdt_entry entry_to_add)
{
??? char key_to_add[CAT_CAT_LEN + 10];
??? datum local_data_datum;
??? datum local_key_datum;
??? int result;
??? if (!cdc_dbm_ptr || !cdt_dbm_ptr) return (0);
??? if (strlen(entry_to_add.catalog) >= CAT_CAT_LEN) return (0);
??? memset(&key_to_add, ‘/0’, sizeof(key_to_add));
??? sprintf(key_to_add, “%s %d”, entry_to_add.catalog,
????????????????? entry_to_add.track_no);
??? local_key_datum.dptr = (void *) key_to_add;
??? local_key_datum.dsize = sizeof(key_to_add);
??? local_data_datum.dptr = (void *) &entry_to_add;
??? local_data_datum.dsize = sizeof(entry_to_add);
??? result = dbm_store(cdt_dbm_ptr, local_key_datum, local_data_datum,
???????????????????????? DBM_REPLACE);
??? /* dbm_store() uses 0 for success and -ve numbers for errors */
??? if (result == 0)
??????? return (1);
??? return (0);
}
11 如果我們可以添加一些東西,我們最好也可以刪除了他們。下面這個函數刪除類別記錄:
int del_cdc_entry(const char *cd_catalog_ptr)
{
??? char key_to_del[CAT_CAT_LEN + 1];
??? datum local_key_datum;
??? int result;
??? if (!cdc_dbm_ptr || !cdt_dbm_ptr) return (0);
??? if (strlen(cd_catalog_ptr) >= CAT_CAT_LEN) return (0);
??? memset(&key_to_del, ‘/0’, sizeof(key_to_del));
??? strcpy(key_to_del, cd_catalog_ptr);
??? local_key_datum.dptr = (void *) key_to_del;
??? local_key_datum.dsize = sizeof(key_to_del);
??? result = dbm_delete(cdc_dbm_ptr, local_key_datum);
??? /* dbm_delete() uses 0 for success */
??? if (result == 0) return (1);
??? return (0);
}
12 下面這個函數等同于刪除一個音軌信息。記住,音軌關鍵字是類別記錄字符串以及一個音軌序號的組合:
int del_cdt_entry(const char *cd_catalog_ptr, const int track_no)
{
??? char key_to_del[CAT_CAT_LEN + 10];
??? datum local_key_datum;
??? int result;
??? if (!cdc_dbm_ptr || !cdt_dbm_ptr) return (0);
??? if (strlen(cd_catalog_ptr) >= CAT_CAT_LEN) return (0);
??? memset(&key_to_del, ‘/0’, sizeof(key_to_del));
??? sprintf(key_to_del, “%s %d”, cd_catalog_ptr, track_no);
??? local_key_datum.dptr = (void *) key_to_del;
??? local_key_datum.dsize = sizeof(key_to_del);
??? result = dbm_delete(cdt_dbm_ptr, local_key_datum);
??? /* dbm_delete() uses 0 for success */
??? if (result == 0) return (1);
??? return (0);
}
13 最后但不并不是不重要的一點,我們需要一個簡單的搜索函數。他并不是非常高級,但是他確實演示了如果在不知道更多關鍵字的情況下如何搜索dbm記錄。
因為我們并不知道存在多少記錄,我們實現這個函數,在每次調用時只返回一個記錄。如果沒有查找到任何記錄,記錄就會空。要搜索整個數據庫,我們通過使用一個指向整數的指針*first_call_ptr來調用這個函數,在第一次調用時此參數值應為1。然后此函數就會知道他應由數據的起始處開始搜索。在接下來的調用中,變量為0,而函數就會上一次他所查找的記錄之后恢復查找。
當我們需要重新啟動我們的查找時,很可能是查找另一個不同的類別記錄,我們必須再一次將*first_call_ptr設置為真來調用這個函數,將參數設置為真用來初始化搜索。
在函數調用之間,函數會維護一些內部狀態信息。這會隱藏客戶繼續查找的復雜性,并且保持查找函數實現的細節。
如果搜索字符串指向一個null字符,此時就會匹配所有的記錄。
cdc_entry search_cdc_entry(const char *cd_catalog_ptr, int *first_call_ptr)
{
??? static int local_first_call = 1;
??? cdc_entry entry_to_return;
??? datum local_data_datum;
??? static datum local_key_datum;??? /* notice this must be static */
??? memset(&entry_to_return, ‘/0’, sizeof(entry_to_return));
14 如平時一樣,我們由必要的檢測開始:
if (!cdc_dbm_ptr || !cdt_dbm_ptr) return (entry_to_return);
if (!cd_catalog_ptr || !first_call_ptr) return (entry_to_return);
if (strlen(cd_catalog_ptr) >= CAT_CAT_LEN) return (entry_to_return);
/* protect against never passing *first_call_ptr true */
if (local_first_call) {
??? local_first_call = 0;
??? *first_call_ptr = 1;
}
15 如果這個函數已經由設置為真的*first_call_ptr參數進行了調用,我們需要由開始(或是重新開始)搜索數據庫的超始處。如果*first_call_ptr并不會為真,我們只是簡單的移到數據中的下一個關鍵字上:
if (*first_call_ptr) {
??? *first_call_ptr = 0;
??? local_key_datum = dbm_firstkey(cdc_dbm_ptr);
}
else {
??? local_key_datum = dbm_nextkey(cdc_dbm_ptr);
}
do {
??? if (local_key_datum.dptr != NULL) {
??????? /* an entry was found */
??????? local_data_datum = dbm_fetch(cdc_dbm_ptr, local_key_datum);
??????? if (local_data_datum.dptr) {
??????????? memcpy(&entry_to_return, (char *) local_data_datum.dptr,
?????????????????? local_data_datum.dsize);
16 我們的搜索程序進行簡單的檢測以確定查找字符串是否包含在當前的類別記錄中。
??????????????? /* check if search string occurs in the entry */
??????????????? if (!strstr(entry_to_return.catalog, cd_catalog_ptr))
??????????????????? {
??????????????????? memset(&entry_to_return, ‘/0’,
????????????????????????????????? sizeof(entry_to_return));
??????????????????? local_key_datum = dbm_nextkey(cdc_dbm_ptr);
??????????????? }
??????????? }
??????? }
??? } while (local_key_datum.dptr &&
?????????? local_data_datum.dptr &&
?????????? (entry_to_return.catalog[0] == ‘/0’));
??? return (entry_to_return);
} /* search_cdc_entry */
現在我們就可以所有的內容放在一個makefile文件中。現在不要太擔心,因為我們會在下一章討論他是如何工作的。就目前而言,輸入下面的內容,并且將其保存為Makefile。
all:???? application
INCLUDE=/usr/include/gdbm
LIBS=gdbm
CFLAGS=
app_ui.o: app_ui.c cd_data.h
???? gcc $(CFLAGS) -c app_ui.c
access.o: access.c cd_data.h
???? gcc $(CFLAGS) -I$(INCLUDE) -c access.c
application:???? app_ui.o access.o
???? gcc $(CFLAGS) -o application app_ui.o access.o -l$(LIBS)
clean:
???? rm -f application *.o
nodbmfiles:
???? rm -f *.dir *.pag
要編譯我們的CD程序,在提示符下輸入下面的命令:
$ make
如果一切順利,就會在當前目錄下編譯成功application可執行程序。
總結
在這一章,我們已經了解了數據管理的三個方面。首先,我們了解了Linux內存系統,以及其使用是如何簡單,盡管按需調度分頁虛擬內存的實現非常復雜。我們同時也會發現Linux系統通過合法的內存訪問來保護操作系統與其他程序。
然后我們探討了文件鎖如何使得多個程序合作訪問數據。我們首先了解了一個簡單的二進制信號量,然而了解一個更為復雜的情況,此是我們鎖住文件的不同部分用于共享或是排他訪問。接下來我們了解了dbm庫,以及其存儲和使用一個非常靈活的索引機制讀取數據的能力。
最后,我們使用dbm庫作為存儲技術重新設計并且實現了我們的CD數據庫程序。
                        
                        
                        我們已經了解了環境以及管理數據的相關內容了,現在是更新程序的時候了。dbm數據庫看起來對于存儲我們的CD信息是十分合適的,所以我們會將dbm數據用作我們的新實現的基礎。
更新設計
因為這次更新涉及到一個重要的代碼重寫,所以現在我們需要看一下我們的設計描述以確定是否需要修改。使用以逗號分隔的可以變化的文件來存儲信息,盡管在Shell中很容易實現,但是已經證明是十分嚴格的了。大量的CD標題以及音軌信息在其中需要大量的逗號。如果我們使用dbm就可以放棄這種分隔方法,所以這是我們的設計需要修改的一個元素。
使用單獨的文件來分離標題與音軌之間的信息,看起來是一個好主意,所以我們也會使用這個邏輯安排。
前面的實現看起來在某種程序上將程序的訪問部分與用戶接口部分相混合,至少是因為他們都在一個單獨的文件中實現。在這個實現中,我們使用一個頭文件來描述數據訪問所需要的數據以及方法,并且將用戶接口與數據操作實現在另一個單獨的文件中。
盡管我們可以繼續保持用戶接口的curses實現,但是我們會回歸到一個簡單的基于命令行的系統。這會使得程序的用戶接口部分更為矮小和簡單,并且允許我們關注于其他的實現部分。
盡管我們在dbm代碼中不能使用SQL,但是我們可以使用SQL術語以及更為通常的方式來表達我們的新數據庫。如果我們不熟悉SQL也不要擔心;我們會解釋這些定義。我們將會在第八章了解更多有關SQL的內容。在代碼中,數據表可以用下面的方法來描述:
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表,他告訴我們對于每一個實現記錄都有一個唯一的類別。對于cdc_entry表,他告訴我們音軌信息號不可以為0,而且catalog與track_no的組合是唯一的。
使用dbm的CD數據庫程序
我們現在將要使用dbm數據來實現我們的程序從而存儲我們需要的信息,使用的文件為cd_data.h,app_ui.c以及cd_access.c。
我們同時要將我們的用戶界面重寫為一個命令行程序。在這本書的后面部分,當我們探討使用不同的客戶/服務器機制來實現在我們的程序,以及最后使用Web瀏覽器跨越網絡來訪問程序時,會重用這個程序的數據庫接口以及部分用戶接口。將接口轉換為一個簡單的命令行驅動的接口可以很容易的使得我們的關注于程序的重要部分,而不是接口。
下面我們將要探討的是頭文件cd_data.h以及在后面的章節中cd_access.c中幾次重用的函數。
試驗--cd_data.h
我們會由頭文件開始,定義我們數據的結構,以及我們將會用于訪問數據的函數。
1 這是為CD數據庫而定義的數據結構。他定義了組成數據庫的兩個數據表的結構以及尺寸。我們將會由定義我們將會用到的數據域的尺寸以及兩個結構開始:一個結構用戶類別記錄,而另一個用于音軌記錄。
/* The catalog table */
#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;
/* The tracks table, one entry per track */
#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_開始的函數則用于音軌記錄。
/* Initialization and termination functions */
int database_initialize(const int new_database);
void database_close(void);
/* two for simple data retrieval */
cdc_entry get_cdc_entry(const char *cd_catalog_ptr);
cdt_entry get_cdt_entry(const char *cd_catalog_ptr, const int track_no);
?????? /* two for data addition */
?????? int add_cdc_entry(const cdc_entry entry_to_add);
?????? int add_cdt_entry(const cdt_entry entry_to_add);
?????? /* two for data deletion */
?????? int del_cdc_entry(const char *cd_catalog_ptr);
?????? int del_cdt_entry(const char *cd_catalog_ptr, const int track_no);
?????? /* one search function */
?????? cdc_entry search_cdc_entry(const char *cd_catalog_ptr, int *first_call_ptr);
試驗--app_ui.c
現在我們開始探討用戶接口。這會給我們一個相對簡單的程序,通過他可以訪問數據庫函數。我們將會在一個單獨的文件中實現這個接口。
1 如平時一樣,我們由頭文件開始:
#define _XOPEN_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include “cd_data.h”
#define TMP_STRING_LEN 125 /* this number must be larger than the biggest
????????????????????????????? single string in any database structure */
2 我們定義我們的菜單選項。在這里使用#defined常量定義的形式,因為他會允許編譯器檢查菜單選項變量的類型。
typedef enum {
??? mo_invalid,
??? mo_add_cat,
??? mo_add_tracks,
??? mo_del_cat,
??? mo_find_cat,
??? mo_list_cat_tracks,
??? mo_del_tracks,
??? mo_count_entries,
??? mo_exit
} menu_options;
3 現在我們編寫局部函數的原型。要記住,實際訪問數據的原型包含在cd_data.h中。
static int command_mode(int argc, char *argv[]);
static void announce(void);
static menu_options show_menu(const cdc_entry *current_cdc);
static int get_confirm(const char *question);
static int enter_new_cat_entry(cdc_entry *entry_to_update);
static void enter_new_track_entries(const cdc_entry *entry_to_add_to);
static void del_cat_entry(const cdc_entry *entry_to_delete);
static void del_track_entries(const cdc_entry *entry_to_delete);
static cdc_entry find_cat(void);
static void list_tracks(const cdc_entry *entry_to_use);
static void count_all_entries(void);
static void display_cdc(const cdc_entry *cdc_to_show);
static void display_cdt(const cdt_entry *cdt_to_show);
static void strip_return(char *string_to_strip);
4 最后我們進行main函數。這個函數由以保證我們用來保持當前選中的CD類別記錄的音軌信息的current_cdc_entry已經進行了初始化而開始的。同時我們也要分析命令行,從而得到所運行的程序,并且初始化數據庫。
void main(int argc, char *argv[])
{
??? menu_options current_option;
??? cdc_entry current_cdc_entry;
??? int command_result;
??? memset(¤t_cdc_entry, ‘/0’, sizeof(current_cdc_entry));
??? if (argc > 1) {
??????? command_result = command_mode(argc, argv);
??????? exit(command_result);
??? }
??? announce();
??? if (!database_initialize(0)) {
??????? fprintf(stderr, “Sorry, unable to initialize database/n”);
??????? fprintf(stderr, “To create a new database use %s -i/n”, argv[0]);
??????? exit(EXIT_FAILURE);
??? }
5 現在我們已經準備好處理用戶輸入了。我們在一個循環中,提示菜單選項,并且進行處理,直到用戶選擇了退出選項。注意,在這里我們將current_cdc_entry結構傳遞給show_menu函數。我們這樣做就要使得如果當前選中了一個類別時菜單選項可以發生變化。
while(current_option != mo_exit) {
??????? current_option = show_menu(¤t_cdc_entry);
??????? switch(current_option) {
??????????? case mo_add_cat:
??????????????? if (enter_new_cat_entry(¤t_cdc_entry)) {
??????????????????? if (!add_cdc_entry(current_cdc_entry)) {
??????????????????????? fprintf(stderr, “Failed to add new entry/n”);
??????????????????????? memset(¤t_cdc_entry, ‘/0’,
??????????????????????????????? sizeof(current_cdc_entry));
??????????????????? }
??????????????? }
??????????????? break;
??????????? case mo_add_tracks:
??????????? enter_new_track_entries(¤t_cdc_entry);
??????????? break;
??????? case mo_del_cat:
??????????? del_cat_entry(¤t_cdc_entry);
??????????? break;
??????? case mo_find_cat:
??????????? current_cdc_entry = find_cat();
??????????? break;
??????? case mo_list_cat_tracks:
??????????? list_tracks(¤t_cdc_entry);
??????????? break;
??????? case mo_del_tracks:
??????????? del_track_entries(¤t_cdc_entry);
??????????? break;
??????? case mo_count_entries:
??????????? count_all_entries();
??????????? break;
??????? case mo_exit:
??????????? break;
??????? case mo_invalid:
??????????? break;
??????? default:
??????????? break;
??? } /* switch */
} /* while */
6 當main循環退出時,我們關閉數據并且退回到環境。通過announce函數來打印歡迎界面:
??? database_close();
??? exit(EXIT_SUCCESS);
} /* main */
static void announce(void)
{
??? printf(“/n/nWelcome to the demonstration CD catalog database /
???????????? program/n”);
}
7 在這里我們實現了show_menu函數。這個函數會檢測當前類別是否使用類別名的第一個字母被選中。如果一個類別被選中就會出現更多的選項。
static menu_options show_menu(const cdc_entry *cdc_selected)
{
??? char tmp_str[TMP_STRING_LEN + 1];
??? menu_options option_chosen = mo_invalid;
? while (option_chosen == mo_invalid) {
????? if (cdc_selected->catalog[0]) {
????????? printf(“/n/nCurrent entry: “);
????????? printf(“%s, %s, %s, %s/n”, cdc_selected->catalog,
???????????????? cdc_selected->title,
???????????????? cdc_selected->type,
???????????????? cdc_selected->artist);
????????? printf(“/n”);
????????? printf(“1 - add new CD/n”);
????????? printf(“2 - search for a CD/n”);
????????? printf(“3 - count the CDs and tracks in the database/n”);
????????? printf(“4 - re-enter tracks for current CD/n”);
????????? printf(“5 - delete this CD, and all its tracks/n”);
????????? printf(“6 - list tracks for this CD/n”);
????????? printf(“q - quit/n”);
????????? printf(“/nOption: “);
????????? fgets(tmp_str, TMP_STRING_LEN, stdin);
????????? switch(tmp_str[0]) {
????????????? case ‘1’: option_chosen =? mo_add_cat; break;
????????????? case ‘2’: option_chosen =? mo_find_cat; break;
????????????? case ‘3’: option_chosen =? mo_count_entries; break;
????????????? case ‘4’: option_chosen =? mo_add_tracks; break;
????????????? case ‘5’: option_chosen =? mo_del_cat; break;
????????????? case ‘6’: option_chosen =? mo_list_cat_tracks; break;
????????????? case ‘q’: option_chosen =? mo_exit; break;
????????? }
????? }
????? else {
????????? printf(“/n/n”);
????????? printf(“1 - add new CD/n”);
????????? printf(“2 - search for a CD/n”);
????????? printf(“3 - count the CDs and tracks in the database/n”);
????????? printf(“q - quit/n”);
????????? printf(“/nOption: “);
????????? fgets(tmp_str, TMP_STRING_LEN, stdin);
????????? switch(tmp_str[0]) {
????????????? case ‘1’: option_chosen = mo_add_cat; break;
????????????? case ‘2’: option_chosen = mo_find_cat; break;
????????????? case ‘3’: option_chosen = mo_count_entries; break;
????????????? case ‘q’: option_chosen = mo_exit; break;
????????? }
????? }
? } /* while */
? return(option_chosen);
}
8 有多個地方我們希望詢問用戶他是否確定所請求的動作。我們并不是在代碼中的多個地方詢問用戶,相反,我們會將其作為一個單獨的get_confirm函數來實現:
static int get_confirm(const char *question)
{
??? char tmp_str[TMP_STRING_LEN + 1];
? printf(“%s”, question);
? fgets(tmp_str, TMP_STRING_LEN, stdin);
? if (tmp_str[0] == ‘Y’ || tmp_str[0] == ‘y’) {
????? return(1);
? }
? return(0);
}
9 函數enter_new_cat_entry允許用戶可以輸入新的類別記錄。但是并不希望存儲由fgets函數所返回的回車,所以我們要去掉回車。
static int enter_new_cat_entry(cdc_entry *entry_to_update)
{
??? cdc_entry new_entry;
??? char tmp_str[TMP_STRING_LEN + 1];
??? memset(&new_entry, ‘/0’, sizeof(new_entry));
??? printf(“Enter catalog entry: “);
??? (void)fgets(tmp_str, TMP_STRING_LEN, stdin);
??? strip_return(tmp_str);
??? strncpy(new_entry.catalog, tmp_str, CAT_CAT_LEN - 1);
??? printf(“Enter title: “);
??? (void)fgets(tmp_str, TMP_STRING_LEN, stdin);
??? strip_return(tmp_str);
??? strncpy(new_entry.title, tmp_str, CAT_TITLE_LEN - 1);
??? printf(“Enter type: “);
??? (void)fgets(tmp_str, TMP_STRING_LEN, stdin);
??? strip_return(tmp_str);
??? strncpy(new_entry.type, tmp_str, CAT_TYPE_LEN - 1);
??? printf(“Enter artist: “);
??? (void)fgets(tmp_str, TMP_STRING_LEN, stdin);
??? strip_return(tmp_str);
??? strncpy(new_entry.artist, tmp_str, CAT_ARTIST_LEN - 1);
??? printf(“/nNew catalog entry entry is :-/n”);
??? display_cdc(&new_entry);
??? if (get_confirm(“Add this entry ?”)) {
??????? memcpy(entry_to_update, &new_entry, sizeof(new_entry));
??????? return(1);
??? }
??? return(0);
}
10 現在我們要來討論輸入音軌信息的enter_new_track_entries函數。相比類別記錄函數,這個函數要復雜一些,因為我們允許保存已經存在的音軌記錄。
static void enter_new_track_entries(const cdc_entry *entry_to_add_to)
{
??? cdt_entry new_track, existing_track;
??? char tmp_str[TMP_STRING_LEN + 1];
??? int track_no = 1;
??? if (entry_to_add_to->catalog[0] == ‘/0’) return;
??? printf(“/nUpdating tracks for %s/n”, entry_to_add_to->catalog);
??? printf(“Press return to leave existing description unchanged,/n”);
??? printf(“ a single d to delete this and remaining tracks,/n”);
??? printf(“ or new track description/n”);
??? while(1) {
11 首先,我們需要檢測是否存在當前音軌號的音軌記錄。依據我們需要查找的內容,我們可以改變提示符。
memset(&new_track, ‘/0’, sizeof(new_track));
existing_track = get_cdt_entry(entry_to_add_to->catalog,
???????????????????????????????? track_no);
if (existing_track.catalog[0]) {
??? printf(“/tTrack %d: %s/n”, track_no,
?????????????????? existing_track.track_txt);
??? printf(“/tNew text: “);
}
else {
??? printf(“/tTrack %d description: “, track_no);
}
fgets(tmp_str, TMP_STRING_LEN, stdin);
strip_return(tmp_str);
12 如果并不存在所要查找的音軌信息,而用戶并沒有添加音軌信息,我們假設用戶并沒有音軌信息需要添加。
if (strlen(tmp_str) == 0) {
??? if (existing_track.catalog[0] == ‘/0’) {
??????????? /* no existing entry, so finished adding */
??????? break;
??? }
??? else {
??????? /* leave existing entry, jump to next track */
??????? track_no++;
??????? continue;
??? }
}
13 如果用戶輸入了一個d字符,這會刪除當前以及更高記錄號的音軌信息。如果并沒有查找到要刪除的音軌信息,del_cat_entry函數會返回false。
if ((strlen(tmp_str) == 1) && tmp_str[0] == ‘d’) {
??????? /* delete this and remaining tracks */
??? while (del_cdt_entry(entry_to_add_to->catalog, track_no)) {
??????? track_no++;
??? }
??? break;
}
14 現在我們要來編碼添加一個新的音軌信息或是更新一個已經存在的音軌信息。我們使用cdt_entry構成new_track,然后調用數據庫函數add_cdt_entry將其添加到數據庫中。
????? strncpy(new_track.track_txt, tmp_str, TRACK_TTEXT_LEN - 1);
????? strcpy(new_track.catalog, entry_to_add_to->catalog);
????? new_track.track_no = track_no;
????? if (!add_cdt_entry(new_track)) {
????????? fprintf(stderr, “Failed to add new track/n”);
????????? break;
????? }
????? track_no++;
? } /* while */
}
15 函數del_cat_entry刪除一個類別記錄。我們絕不會允許一個不存在類別的音軌記錄存在。
static void del_cat_entry(const cdc_entry *entry_to_delete)
{
??? int track_no = 1;
??? int delete_ok;
??? display_cdc(entry_to_delete);
??? if (get_confirm(“Delete this entry and all it’s tracks? “)) {
??????? do {
??????????? delete_ok = del_cdt_entry(entry_to_delete->catalog,
?????????????????????????????????????? track_no);
??????????? track_no++;
??????? } while(delete_ok);
??????? if (!del_cdc_entry(entry_to_delete->catalog)) {
??????????? fprintf(stderr, “Failed to delete entry/n”);
??????? }
??? }
}
16 下一個函數是刪除一個類別的所有音軌信息的程序:
static void del_track_entries(const cdc_entry *entry_to_delete)
{
??? int track_no = 1;
??? int delete_ok;
??? display_cdc(entry_to_delete);
??? if (get_confirm(“Delete tracks for this entry? “)) {
??????? do {
??????????? delete_ok = del_cdt_entry(entry_to_delete->catalog, track_no);
??????????? track_no++;
??????? } while(delete_ok);
??? }
}
17 下面,我們創建一個非常簡單的類別查找程序。我們允許用戶輸入一個字符串,然后檢測包含這個字符串的類別。因為也許會匹配多個記錄,我們只是簡單的依次用戶提供匹配的記錄:
static cdc_entry find_cat(void)
{
??? cdc_entry item_found;
??? char tmp_str[TMP_STRING_LEN + 1];
??? int first_call = 1;
??? int any_entry_found = 0;
??? int string_ok;
??? int entry_selected = 0;
??? do {
??????? string_ok = 1;
??????? printf(“Enter string to search for in catalog entry: “);
??????? fgets(tmp_str, TMP_STRING_LEN, stdin);
??????? strip_return(tmp_str);
??????? if (strlen(tmp_str) > CAT_CAT_LEN) {
??????????? fprintf(stderr, “Sorry, string too long, maximum %d /
????????????????????????????? characters/n”, CAT_CAT_LEN);
??????????? string_ok = 0;
??????? }
??? } while (!string_ok);
??? while (!entry_selected) {
??????? item_found = search_cdc_entry(tmp_str, &first_call);
??????? if (item_found.catalog[0] != ‘/0’) {
??????????? any_entry_found = 1;
??????????? printf(“/n”);
??????????? display_cdc(&item_found);
??????????? if (get_confirm(“This entry? “)) {
??????????????? entry_selected = 1;
??????????? }
??????? }
??????? else {
??????????? if (any_entry_found) printf(“Sorry, no more matches found/n”);
??????????? else printf(“Sorry, nothing found/n”);
??????????? break;
????? }
? }
? return(item_found);
}
18 list_tracks是一個打印一個指定的類別內的所音軌的函數:
static void list_tracks(const cdc_entry *entry_to_use)
{
??? int track_no = 1;
??? cdt_entry entry_found;
??? display_cdc(entry_to_use);
??? printf(“/nTracks/n”);
??? do {
??????????? entry_found = get_cdt_entry(entry_to_use->catalog,
??????????????????????????????????????? track_no);
??????????? if (entry_found.catalog[0]) {
??????????????? display_cdt(&entry_found);
??????????????? track_no++;
??????????? }
??? } while(entry_found.catalog[0]);
??? (void)get_confirm(“Press return”);
} /* list_tracks */
19 count_all_entries函數統計所有的音軌信息記錄數:
static void count_all_entries(void)
{
??? int cd_entries_found = 0;
??? int track_entries_found = 0;
??? cdc_entry cdc_found;
??? cdt_entry cdt_found;
??? int track_no = 1;
??? int first_time = 1;
??? char *search_string = “”;
??? do {
??????? cdc_found = search_cdc_entry(search_string, &first_time);
??????? if (cdc_found.catalog[0]) {
??????????? cd_entries_found++;
??????????? track_no = 1;
??????????? do {
??????????????? cdt_found = get_cdt_entry(cdc_found.catalog, track_no);
??????????????? if (cdt_found.catalog[0]) {
??????????????????? track_entries_found++;
??????????????????? track_no++;
??????????????? }
??????????? } while (cdt_found.catalog[0]);
??????? }
??? } while (cdc_found.catalog[0]);
??? printf(“Found %d CDs, with a total of %d tracks/n”, cd_entries_found,
??????????????? track_entries_found);
??? (void)get_confirm(“Press return”);
}
20 現在我們編寫display_cdc函數,一個用于顯示一個類別記錄的函數:
static void display_cdc(const cdc_entry *cdc_to_show)
{
??? printf(“Catalog: %s/n”, cdc_to_show->catalog);
??? printf(“/ttitle: %s/n”, cdc_to_show->title);
??? printf(“/ttype: %s/n”, cdc_to_show->type);
??? printf(“/tartist: %s/n”, cdc_to_show->artist);
}
以及用于顯示單一音軌記錄的display_cdt函數:
static void display_cdt(const cdt_entry *cdt_to_show)
{
??? printf(“%d: %s/n”, cdt_to_show->track_no, cdt_to_show->track_txt);
}
21 函數strip_return用于移除字符串末尾的回車符。記住,與Unix類似,在Linux中,使用一個回車來代表一行的結束:
static void strip_return(char *string_to_strip)
{
??? int len;
??? len = strlen(string_to_strip);
??? if (string_to_strip[len - 1] == ‘/n’) string_to_strip[len - 1] = ‘/0’;
}
22 command_mode是一個用于分析命令行參數的函數。getopt函數是一個很好的方法,可以用來保證我們的程序接受符合標準Linux約定的參數。
static int command_mode(int argc, char *argv[])
{
??? int c;
??? int result = EXIT_SUCCESS;
??? char *prog_name = argv[0];
??? /* these externals used by getopt */
??? extern char *optarg;
??? extern optind, opterr, optopt;
??? while ((c = getopt(argc, argv, “:i”)) != -1) {
??????? switch(c) {
??????????? case ‘i’:
??????????????? if (!database_initialize(1)) {
??????????????????? result = EXIT_FAILURE;
??????????????????? fprintf(stderr, “Failed to initialize database/n”);
??????????????? }
??????????????? break;
??????????? case ‘:’:
??????????? case ‘?’:
??????????? default:
????????????? fprintf(stderr, “Usage: %s [-i]/n”, prog_name);
????????????? result = EXIT_FAILURE;
????????????? break;
????? } /* switch */
? } /* while */
? return(result);
}
試驗--cd_access.c
現在我們來討論訪問dbm數據的函數。
1 如平時一樣,我們由#include語句開始。然后我們使用#define語句來指定我們將會用于存儲數據文件。
#define _XOPEN_SOURCE
#include? <unistd.h>
#include? <stdlib.h>
#include? <stdio.h>
#include? <fcntl.h>
#include? <string.h>
#include? <ndbm.h>
#include “cd_data.h”
#define? CDC_FILE_BASE “cdc_data”
#define? CDT_FILE_BASE “cdt_data”
#define? CDC_FILE_DIR “cdc_data.dir”
#define? CDC_FILE_PAG “cdc_data.pag”
#define? CDT_FILE_DIR “cdt_data.dir”
#define? CDT_FILE_PAG “cdt_data.pag”
2 我們使用下面的兩個局部變量來保存當前數據庫的信息:
static DBM *cdc_dbm_ptr = NULL;
static DBM *cdt_dbm_ptr = NULL;
3 默認情況下,database_initialize函數會打開一個已經存在數據庫,但是通過傳遞一個非零的參數new_database,我們可以強制其創建一個新的數據庫,但是移除當前已存在的數據庫。如果數據庫成功的進行初始化,兩個數據庫也會進行初始,來表明已打開了一個數據庫。
int database_initialize(const int new_database)
{
??? int open_mode = O_CREAT | O_RDWR;
??? /* If any existing database is open then close it */
??? if (cdc_dbm_ptr) dbm_close(cdc_dbm_ptr);
??? if (cdt_dbm_ptr) dbm_close(cdt_dbm_ptr);
??? if (new_database) {
??????? /* delete the old files */
????? (void) unlink(CDC_FILE_PAG);
????? (void) unlink(CDC_FILE_DIR);
????? (void) unlink(CDT_FILE_PAG);
????? (void) unlink(CDT_FILE_DIR);
? }
? /* Open some new files, creating them if required */
? cdc_dbm_ptr = dbm_open(CDC_FILE_BASE, open_mode, 0644);
? cdt_dbm_ptr = dbm_open(CDT_FILE_BASE, open_mode, 0644);
? if (!cdc_dbm_ptr || !cdt_dbm_ptr) {
????? fprintf(stderr, “Unable to create database/n”);
????? cdc_dbm_ptr = cdt_dbm_ptr = NULL;
????? return (0);
? }
? return (1);
}
4 database_close函數只是簡單的關閉所打開的數據庫,并且設置兩個數據庫指針指向null來表明當前沒有數據庫打開。
void database_close(void)
{
??? if (cdc_dbm_ptr) dbm_close(cdc_dbm_ptr);
??? if (cdt_dbm_ptr) dbm_close(cdt_dbm_ptr);
??? cdc_dbm_ptr = cdt_dbm_ptr = NULL;
}
5 接下來我們會編寫一個函數,當向這個函數傳遞一個類別字符串時,此函數會取回此類別記錄。如果沒有找到此記錄,返回的數據會有一個空的類別區域。
cdc_entry get_cdc_entry(const char *cd_catalog_ptr)
{
??? cdc_entry entry_to_return;
??? char entry_to_find[CAT_CAT_LEN + 1];
??? datum local_data_datum;
??? datum local_key_datum;
??? memset(&entry_to_return, ‘/0’, sizeof(entry_to_return));
6 我們會在開始時進行一些檢測,來保證數據庫已經打開,并且我們傳遞了合理的參數--也就是說,查找關鍵字只包含可用的字符串以及null。
if (!cdc_dbm_ptr || !cdt_dbm_ptr) return (entry_to_return);
if (!cd_catalog_ptr) return (entry_to_return);
if (strlen(cd_catalog_ptr) >= CAT_CAT_LEN) return (entry_to_return);
memset(&entry_to_find, ‘/0’, sizeof(entry_to_find));
strcpy(entry_to_find, cd_catalog_ptr);
7 我們設置dbm函數所需要的datum結構,然后使用dbm_fetch函數來取出數據。如果沒有取回任何數據,我們就會返回我們先前所初始化的空的entry_to_return結構。
??? local_key_datum.dptr = (void *) entry_to_find;
??? local_key_datum.dsize = sizeof(entry_to_find);
??? memset(&local_data_datum, ‘/0’, sizeof(local_data_datum));
??? local_data_datum = dbm_fetch(cdc_dbm_ptr, local_key_datum);
??? if (local_data_datum.dptr) {
??????? memcpy(&entry_to_return, (char *)local_data_datum.dptr,
?????????????????? local_data_datum.dsize);
??? }
??? return (entry_to_return);
} /* get_cdc_entry */
8 我們最好也可以得到一個單一的音軌信息,與get_cdc_entry函數相類似,而這也正是下一個函數所要做的,但是需要一個指向類別字符的指針以及一個音軌序號作為參數。
cdt_entry get_cdt_entry(const char *cd_catalog_ptr, const int track_no)
{
??? cdt_entry entry_to_return;
??? char entry_to_find[CAT_CAT_LEN + 10];
??? datum local_data_datum;
??? datum local_key_datum;
??? memset(&entry_to_return, ‘/0’, sizeof(entry_to_return));
??? if (!cdc_dbm_ptr || !cdt_dbm_ptr) return (entry_to_return);
??? if (!cd_catalog_ptr) return (entry_to_return);
??? if (strlen(cd_catalog_ptr) >= CAT_CAT_LEN) return (entry_to_return);
??? /* set up the search key, which is a composite key of catalog entry
?????? and track number */
??? memset(&entry_to_find, ‘/0’, sizeof(entry_to_find));
??? sprintf(entry_to_find, “%s %d”, cd_catalog_ptr, track_no);
??? local_key_datum.dptr = (void *) entry_to_find;
??? local_key_datum.dsize = sizeof(entry_to_find);
??? memset(&local_data_datum, ‘/0’, sizeof(local_data_datum));
??? local_data_datum = dbm_fetch(cdt_dbm_ptr, local_key_datum);
??? if (local_data_datum.dptr) {
??????? memcpy(&entry_to_return, (char *) local_data_datum.dptr,
?????????????????? local_data_datum.dsize);
??? }
??? return (entry_to_return);
}
9 下一個函數,add_cdc_entry,添加一個新的類別記錄:
int add_cdc_entry(const cdc_entry entry_to_add)
{
??? char key_to_add[CAT_CAT_LEN + 1];
??? datum local_data_datum;
??? datum local_key_datum;
??? int result;
? /* check database initialized and parameters valid */
? if (!cdc_dbm_ptr || !cdt_dbm_ptr) return (0);
? if (strlen(entry_to_add.catalog) >= CAT_CAT_LEN) return (0);
? /* ensure the search key contains only the valid string and nulls */
? memset(&key_to_add, ‘/0’, sizeof(key_to_add));
? strcpy(key_to_add, entry_to_add.catalog);
? local_key_datum.dptr = (void *) key_to_add;
? local_key_datum.dsize = sizeof(key_to_add);
? local_data_datum.dptr = (void *) &entry_to_add;
? local_data_datum.dsize = sizeof(entry_to_add);
? result = dbm_store(cdc_dbm_ptr, local_key_datum, local_data_datum,
????????????????????? DBM_REPLACE);
? /* dbm_store() uses 0 for success */
? if (result == 0) return (1);
? return (0);
}
10 add_cdt_entry添加一新的音軌記錄。訪問關鍵字是類別字符串,并且音軌序號作為組合。
int add_cdt_entry(const cdt_entry entry_to_add)
{
??? char key_to_add[CAT_CAT_LEN + 10];
??? datum local_data_datum;
??? datum local_key_datum;
??? int result;
??? if (!cdc_dbm_ptr || !cdt_dbm_ptr) return (0);
??? if (strlen(entry_to_add.catalog) >= CAT_CAT_LEN) return (0);
??? memset(&key_to_add, ‘/0’, sizeof(key_to_add));
??? sprintf(key_to_add, “%s %d”, entry_to_add.catalog,
????????????????? entry_to_add.track_no);
??? local_key_datum.dptr = (void *) key_to_add;
??? local_key_datum.dsize = sizeof(key_to_add);
??? local_data_datum.dptr = (void *) &entry_to_add;
??? local_data_datum.dsize = sizeof(entry_to_add);
??? result = dbm_store(cdt_dbm_ptr, local_key_datum, local_data_datum,
???????????????????????? DBM_REPLACE);
??? /* dbm_store() uses 0 for success and -ve numbers for errors */
??? if (result == 0)
??????? return (1);
??? return (0);
}
11 如果我們可以添加一些東西,我們最好也可以刪除了他們。下面這個函數刪除類別記錄:
int del_cdc_entry(const char *cd_catalog_ptr)
{
??? char key_to_del[CAT_CAT_LEN + 1];
??? datum local_key_datum;
??? int result;
??? if (!cdc_dbm_ptr || !cdt_dbm_ptr) return (0);
??? if (strlen(cd_catalog_ptr) >= CAT_CAT_LEN) return (0);
??? memset(&key_to_del, ‘/0’, sizeof(key_to_del));
??? strcpy(key_to_del, cd_catalog_ptr);
??? local_key_datum.dptr = (void *) key_to_del;
??? local_key_datum.dsize = sizeof(key_to_del);
??? result = dbm_delete(cdc_dbm_ptr, local_key_datum);
??? /* dbm_delete() uses 0 for success */
??? if (result == 0) return (1);
??? return (0);
}
12 下面這個函數等同于刪除一個音軌信息。記住,音軌關鍵字是類別記錄字符串以及一個音軌序號的組合:
int del_cdt_entry(const char *cd_catalog_ptr, const int track_no)
{
??? char key_to_del[CAT_CAT_LEN + 10];
??? datum local_key_datum;
??? int result;
??? if (!cdc_dbm_ptr || !cdt_dbm_ptr) return (0);
??? if (strlen(cd_catalog_ptr) >= CAT_CAT_LEN) return (0);
??? memset(&key_to_del, ‘/0’, sizeof(key_to_del));
??? sprintf(key_to_del, “%s %d”, cd_catalog_ptr, track_no);
??? local_key_datum.dptr = (void *) key_to_del;
??? local_key_datum.dsize = sizeof(key_to_del);
??? result = dbm_delete(cdt_dbm_ptr, local_key_datum);
??? /* dbm_delete() uses 0 for success */
??? if (result == 0) return (1);
??? return (0);
}
13 最后但不并不是不重要的一點,我們需要一個簡單的搜索函數。他并不是非常高級,但是他確實演示了如果在不知道更多關鍵字的情況下如何搜索dbm記錄。
因為我們并不知道存在多少記錄,我們實現這個函數,在每次調用時只返回一個記錄。如果沒有查找到任何記錄,記錄就會空。要搜索整個數據庫,我們通過使用一個指向整數的指針*first_call_ptr來調用這個函數,在第一次調用時此參數值應為1。然后此函數就會知道他應由數據的起始處開始搜索。在接下來的調用中,變量為0,而函數就會上一次他所查找的記錄之后恢復查找。
當我們需要重新啟動我們的查找時,很可能是查找另一個不同的類別記錄,我們必須再一次將*first_call_ptr設置為真來調用這個函數,將參數設置為真用來初始化搜索。
在函數調用之間,函數會維護一些內部狀態信息。這會隱藏客戶繼續查找的復雜性,并且保持查找函數實現的細節。
如果搜索字符串指向一個null字符,此時就會匹配所有的記錄。
cdc_entry search_cdc_entry(const char *cd_catalog_ptr, int *first_call_ptr)
{
??? static int local_first_call = 1;
??? cdc_entry entry_to_return;
??? datum local_data_datum;
??? static datum local_key_datum;??? /* notice this must be static */
??? memset(&entry_to_return, ‘/0’, sizeof(entry_to_return));
14 如平時一樣,我們由必要的檢測開始:
if (!cdc_dbm_ptr || !cdt_dbm_ptr) return (entry_to_return);
if (!cd_catalog_ptr || !first_call_ptr) return (entry_to_return);
if (strlen(cd_catalog_ptr) >= CAT_CAT_LEN) return (entry_to_return);
/* protect against never passing *first_call_ptr true */
if (local_first_call) {
??? local_first_call = 0;
??? *first_call_ptr = 1;
}
15 如果這個函數已經由設置為真的*first_call_ptr參數進行了調用,我們需要由開始(或是重新開始)搜索數據庫的超始處。如果*first_call_ptr并不會為真,我們只是簡單的移到數據中的下一個關鍵字上:
if (*first_call_ptr) {
??? *first_call_ptr = 0;
??? local_key_datum = dbm_firstkey(cdc_dbm_ptr);
}
else {
??? local_key_datum = dbm_nextkey(cdc_dbm_ptr);
}
do {
??? if (local_key_datum.dptr != NULL) {
??????? /* an entry was found */
??????? local_data_datum = dbm_fetch(cdc_dbm_ptr, local_key_datum);
??????? if (local_data_datum.dptr) {
??????????? memcpy(&entry_to_return, (char *) local_data_datum.dptr,
?????????????????? local_data_datum.dsize);
16 我們的搜索程序進行簡單的檢測以確定查找字符串是否包含在當前的類別記錄中。
??????????????? /* check if search string occurs in the entry */
??????????????? if (!strstr(entry_to_return.catalog, cd_catalog_ptr))
??????????????????? {
??????????????????? memset(&entry_to_return, ‘/0’,
????????????????????????????????? sizeof(entry_to_return));
??????????????????? local_key_datum = dbm_nextkey(cdc_dbm_ptr);
??????????????? }
??????????? }
??????? }
??? } while (local_key_datum.dptr &&
?????????? local_data_datum.dptr &&
?????????? (entry_to_return.catalog[0] == ‘/0’));
??? return (entry_to_return);
} /* search_cdc_entry */
現在我們就可以所有的內容放在一個makefile文件中。現在不要太擔心,因為我們會在下一章討論他是如何工作的。就目前而言,輸入下面的內容,并且將其保存為Makefile。
all:???? application
INCLUDE=/usr/include/gdbm
LIBS=gdbm
CFLAGS=
app_ui.o: app_ui.c cd_data.h
???? gcc $(CFLAGS) -c app_ui.c
access.o: access.c cd_data.h
???? gcc $(CFLAGS) -I$(INCLUDE) -c access.c
application:???? app_ui.o access.o
???? gcc $(CFLAGS) -o application app_ui.o access.o -l$(LIBS)
clean:
???? rm -f application *.o
nodbmfiles:
???? rm -f *.dir *.pag
要編譯我們的CD程序,在提示符下輸入下面的命令:
$ make
如果一切順利,就會在當前目錄下編譯成功application可執行程序。
總結
在這一章,我們已經了解了數據管理的三個方面。首先,我們了解了Linux內存系統,以及其使用是如何簡單,盡管按需調度分頁虛擬內存的實現非常復雜。我們同時也會發現Linux系統通過合法的內存訪問來保護操作系統與其他程序。
然后我們探討了文件鎖如何使得多個程序合作訪問數據。我們首先了解了一個簡單的二進制信號量,然而了解一個更為復雜的情況,此是我們鎖住文件的不同部分用于共享或是排他訪問。接下來我們了解了dbm庫,以及其存儲和使用一個非常靈活的索引機制讀取數據的能力。
最后,我們使用dbm庫作為存儲技術重新設計并且實現了我們的CD數據庫程序。
轉載于:https://www.cnblogs.com/dyllove98/archive/2009/01/10/2461967.html
總結
以上是生活随笔為你收集整理的数据管理(八)--CD程序的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: Javascript称球
- 下一篇: 2009年网页设计10大趋势
