GDBM - GNU database manager,一套简单的资料库管理函数。 gdbm 的常见版本是1.73 ,在大部份的FreeBSD, Linux 系统中皆已提供,若未提供, 则可利用搜寻引擎寻找gdbm-1.7.3.tar.gz 或gdbm-1.8.0.tar.gz 并下载,利用 configure 指令即可轻松安装。 在安装时,可以选择是否一并安装dbm 及ndbm 的相容介面。 dbm 是UNIX 上一套古老的常见资料库处理介面,而ndbm 则是改良dbm 缺失而设计的, 两者的介面差异颇大。 gdbm 则是根据ndbm 的介面设计的,两者间非常相似, ndbm 的函数名称皆以dbm_ 开头, 而gdbm 为了加以区别,则以gdbm_ 开头,除此之外,皆可对应。 gdbm 所提供的资料库管理介面,与ndbm 非常相似,因此gdbm 的说明,也可以适用于 ndbm 上。 可用两种方式判断有无安装 gdbm : 1.查询有无 gdbm 的 manpage $ man gdbm 2.查询有无 gdbm 的 library $ ls /usr/lib/libgdbm* gdbm 提供一套管理key/data 类型的资料库的函数,其特征是资料库中,每笔记录都有 一个唯一的键值。 这种key/data 格式的资料档案其实很常见,例如: -------------------------------------------- rock: 983-1231, rock@isu.edu.tw xyz: 931-4321, xyz@touc.edu.tw -------------------------------------------- 一个键值,一个记录内容。 在使用gdbm 前,通常我们会用循序档来存放这类的资料,一笔一行或一区块,一 笔一笔的存下来,这样的好处是: 1.修改方便 可以用一般的文字编辑工具增修资料,不用另行设计。 2.节省空间 有多少资料就用多少空间,不像用struct 的随机档,不论资料量多少,所有 记录都占同样的空间。 但其缺点有: 1.管理不便 当记录数达数百、甚至数万笔时,继续用这种方式管理,可是件令人痛苦的事。 2.使用负担大 资料搜寻费时,所有资料都必须从头开始找,在增修资料时也很费功夫,当资料 量大、使用率高时,会对系统造成负担。 而gdbm 则是针对此缺点提出的一个解决方案,其利用hash 表存放键值,大幅节省 搜寻时间,同时仍允许记录采用浮动长度,在时间及空间两者中取得平衡。 其hash 表是用可扩充式hash 表演算法,会视需要自动扩充hash 表,不需担心 hash 表的大小。 gdbm 将资料库管理动作分成三个: 1.storing 储存 2.retrieval 撷取 3.deletion 删除 加上开启资料库及走访(visiting)的动作,提供七个主要的函数。 再加上一个存放上述函数呼叫错误状态值的变数gdbm_errno 。 其 prototype 如下: ================================================= #include char *dptr; int dsize; } datum; extern gdbm_error gdbm_errno; GDBM_FILE gdbm_open (name, block_size, read_write, mode, fatal_func); char * name; int block_size, read_write, mode; void (*fatal_func) (); void gdbm_close (dbf); GDBM_FILE dbf; int gdbm_store (dbf, key, content, flag); GDBM_FILE dbf; datum key, content; int flag; datum gdbm_fetch (dbf, key); GDBM_FILE dbf; datum key; int gdbm_delete (dbf, key); GDBM_FILE dbf; datum key; datum gdbm_firstkey (dbf); GDBM_FILE dbf; datum gdbm_nextkey (dbf, key); GDBM_FILE dbf; datum key; ================================================= datum 是用来表示记录键值及记录内容的structure 。 1.gdbm_open() 开启资料库。 gdbm 将开启者分成reader 及writer ,在同一时间内可以有多个reader , 但一次只能有一个 writer 。当指定的资料库有reader 在使用时,其他人就无 法成为writer ,当有writer 在使用时,其他人就无法成为reader 或writer。 GDBM_FILE 是传回给开启者的 handle 值。 name 是资料库的档案名称,此档案是完整的, gdbm 不会添加其他字眼上去。 而gdbm 也就只会建立这一个资料库档案,不会另外再建索引档或关连档。 block_size 在建立资料库时指定,指示gdbm 以多大的记忆体区块来读写磁碟资 料,此值最小是512 (bytes) ,但不妨指定0 ,由gdbm 自行根据档案系统的状 态决定。 read_write 在指示开启者的开启型式,可指定的有四: GDBM_READER 成为 reader GDBM_WRITER 成为 writer GDBM_WRCREAT 成为writer ,若资料库不存在则建立之 GDBM_NEWDB 成为writer ,并建立新的资料库,不论是否存在旧资料库 若欲成为writer ,则可以再附带一个设定值GDBM_FAST ,指示gdbm 在写入资 料时,不要马上进行与磁碟内容的同步化动作。 此行为在 gdbm 1.8 后,为预设动作。 mode 在指示建立资料库时,档案的属性,其意义同chmod() 。 一般设为0644 (rw-r--r--),若没有建立资料库的动作,则指定0 即可。 Fatal_func 为函数指标,当开启资料库,发现一个错误时,就呼叫该函数, 一般皆设为NULL ,由gdbm 自行以预设动作处理。 当gdbm 无法成为reader 或writer 时,会立即返回,而不会自动搁置, 此时gdbm_open() 传回NULL ,可由gdbm_errno 得知错误状态码。 范例: --------------------------------------- GDBM_FILE dbf; dbf = gdbm_open("mydata.db", 0, GDBM_READER, 0, NULL); if( dbf == NULL ) { printf("Can not open database\n, %s\n", gdbm_strerror(gdbm_errno) ); } --------------------------------------- 2.gdbm_close() 关闭资料库,不会有错误传回。 范例: --------------------------------------- gdbm_close(dbf); --------------------------------------- 3.gdbm_store() 储存记录。 将记录键值及记录内容存入资料库中,存入动作分为增加及替代,在flag 中指定, 其值有二: GDBM_INSERT 增加一笔新记录,若键值已存在,则储存动作失败。 GDBM_REPLACE 替代记录内容。 其传回值有三: -1 无法储存。 0 储存成功。 1 当使用GDBM_INSERT 时,键值已存在,无法储存。 范例: --------------------------------------- char keybuf[256], databuf[256]; datum key, data; gets(keybuf); gets(databuf); key.dptr = keybuf; key.dsize = strlen(keybuf); data.dptr = databuf; data.dsize = strlen(databuf); rc = gdbm_store(dbf, key, data, GDBM_INSERT); if( rc != 0 ) { printf("Can not store record\n, %s\n", gdbm_strerror(gdbm_errno) ); } --------------------------------------- 注意,这里示范的是资料键值及内容都是一般文字,如果键值及内容中,包 含'\0' 时,就不能用strlen() 去计算size 了。 4.gdbm_fetch() 根据指定键值撷取记录。 传回记录的内容,要注意的是,记录内容的空间是由gdbm 以malloc() 配置的, 但gdbm 不会自动释放,故当你不再使用该资料时,记得将其释放。 若记录内容指向 NULL ,表无此记录。 范例: --------------------------------------- char keybuf[256]; datum key, data; gets(keybuf); key.dptr = keybuf; key.dsize = strlen(keybuf); data = gdbm_fetch(dbf, key); if( data.dptr == NULL ) { puts("Record not found!\n"); } else { printf("Record found (%d): %s", data.dsize, data.dptr); free(data.dptr); } --------------------------------------- 若你只是单纯地想查询键值是否已存在于资料库,而不想撷取资料内容时,可以使 用gdbm_exists(dbf, key) ,其传回非零值(true)时,表示键值存在。 5.gdbm_delete() 根据指定键值移除记录。 传回0 表示成功删除, -1 表示删除失败。 范例: --------------------------------------- char keybuf[256]; datum key; gets(keybuf); key.dptr = keybuf; key.dsize = strlen(keybuf); rc = gdbm_delete(dbf, key); if( rc != 0 ) printf("Record can not delete.\n %s", gdbm_strerror(gdbm_errno)); --------------------------------------- 6.gdbm_firstkey() / gdbm_nextkey() 走访资料库。 根据hash 表走访资料库,由于其并非根据资料存入顺序或键值的排序走访,因此 走访结果并无次序,但可以保证的是,每笔记录都会被走访一次,不会遗漏。 key.dptr 是由gdbm 以malloc() 配置的,但gdbm 不会自动释放,故当你走访完 毕时,记得将其释放。 当key.dptr 指向NULL 时,表示走访完毕。 注意,当你在走访资料库时,不要增加或删除任何记录,因为这将改变hash 表的 内容,造成其走访时的遗漏。 范例: --------------------------------------- datum key, nextkey; int n = 1; puts("Key list:\n"); key = gdbm_firstkey(dbf); while( key.dptr != NULL ) { printf("%d: %s", n, key.dptr); n++; nextkey = gdbm_nextkey(dbf, key); free(key.dptr); key = nextkey; } --------------------------------------- 7.gdbm_reorganize() 重整资料库。 这个函数使用时,请再三斟酌,不要常使用。 随着资料库的增删,时间一久,资料库中,就会留下许多未被使用的空隙,此时可 呼叫gdbm_reorganize(dbf) 进行资料库的重整,去除那些空隙,适度缩减资料库 档案的大小。 除非呼叫gdbm_reorganize() ,否则gdbm 不会缩减资料库档案的长度,但是gdbm 也不是一直往后增加资料,当旧有资料被删除时, gdbm 会尽然重复使用那些空下来 的空间。 附注: 1.参考文件 GDBM(3), gdbm.h 详细说明请参阅此 manpage 。 $ man gdbm 2.在本文中,一共提到了十个gdbm 函数及一个gdbm 变数: gdbm_open() gdbm_close() gdbm_store() gdbm_fetch() gdbm_delete() gdbm_firstkey() gdbm_nextkey() gdbm_exists() gdbm_reorganize() gdbm_strerror() gdbm_errno reader 无法使用gdbm_store(), gdbm_delete() 及gdbm_reorganize() 。 |
|