上一篇: iOS 标准库中常用数据结构和算法之哈希表
KV 数据库
对于结构化数据的存储一般我们使用关系型数据库, 而对于基于 key-value 类型的数据存储则不适合用关系型数据库. 因此 iOS 系统也内置了一套基于 key-value 存储的文件数据库: ndbm.
功能:
一套基于 key-value 形式存储的数据库.
头文件: #include <ndbm.h>
平台: BSD Unix
1. 创建, 打开, 关闭
功能: 数据库文件的创建, 打开, 关闭.
函数签名
- // 数据库文件的创建或者打开
- DBM * dbm_open(const char *file, int open_flags, mode_t file_mode);
- // 数据库文件的关闭
- void dbm_close(DBM *db);
参数:
file:[in] 指定数据库的文件名, 系统在打开和创建时会在内部自动添加. db 的后缀名称, 因此我们不需要指定后缀扩展名部分.
open_flags: [in] 文件的打开属性, 一般传递 O_RDWR | O_CREAT 表明读写以及不存在时创建.
file_mode:[in] 文件的访问权限模式, 一般设置为 0660.
db:[in] 用于执行数据库关闭的句柄, 这个句柄是由数据库文件打开所返回的.
return:[out] 数据库创建成功时返回的数据库句柄指针, 数据库句柄指针是一个 DBM 类型的数据, 这个类型对我们透明, 也不需要我们去关心, 当打开失败时返回 NULL.
2. 添加
功能: 将某个 key-value 键值对添加到数据库中. 系统并没有对 key-value 的内容做限制, 但是在进行添加处理时必须要转化为如下定义的结构体:
- typedef struct {
- void *dptr; // 内存数据的地址
- size_t dsize; // 内存数据的尺寸.
- } datum;
函数签名:
int dbm_store(DBM *db, datum key, datum content, int store_mode);
参数:
db: [in] 数据库句柄.
key:[in] 要插入的 key 部分的内容.
content:[in] 要插入的 value 部分的内容.
store_mode:[in] 插入的模式, 可以指定为 DBM_INSERT 或 DBM_REPLACE. 当指定为 DBM_INSERT 时表明是插入, 如果此时数据库中存在对应的 key 时则此次操作会返回失败; 当指定为 DBM_REPLACE 时则当不存在时会执行添加处理, 而当对应的 key 存在时就会执行替换处理.
return:[out] 当添加成功时返回 0, 当返回 1 时表明插入一个已经存在的 key, 当返回 - 1 时表明插入失败.
删除
功能: 从数据库中删除某个 key-value 键值对.
函数签名:
int dbm_delete(DBM *db, datum key);
参数:
db: [in] 数据库句柄.
key:[in] 要删除的键.
return:[out] 如果返回 0 则删除成功, 返回 1 则表明不存在指定的 key, 返回 - 1 则是其他错误.
查询
功能: 根据指定的 key 从数据库中查找对应的 value 值.
函数签名:
datum dbm_fetch(DBM *db, datum key);
参数
db:[in] 数据库句柄.
key:[in] 查找指定的 key
return:[out] 系统返回一个结构体 datum, 存储返回的 value 值. 如果 key 没有对应的 value 值, 那么返回的 datum 中的 dptr 的值将是 NULL. 我们不需要对返回的值进行内存释放, 也不能对返回的值的内容进行修改.
遍历
功能: 系统提供了两个遍历的函数, 分别是获取整个数据库中最开始的 key 值, 以及获取下一个有效的 key 值的函数.
函数签名:
- // 获取第一个存储的 key 值.
- datum dbm_firstkey(DBM *db);
- // 获取下一个存储的 key 值.
- datum dbm_nextkey(DBM *db);
参数:
db:[in] 数据库句柄
return: 返回对应的 key 值, 如果没有 key 值那么返回的结构体中的 dptr 的值将是 NULL.
描述:
你可以通过这两个函数来依次遍历整个数据库中的 key 值, 然后再结合 dbm_fetch 来获取对应的 value. 需要注意的是如果某次遍历期间中执行了插入或者删除操作则应该要重新进行遍历, 否则得到的结果未可知.
示例代码:
- // 遍历函数
- void traversendbm(DBM *dbm)
- {
- printf("start:-------------\n");
- datum key = dbm_firstkey(dbm);
- while (key.dptr != NULL)
- {
- datum val = dbm_fetch(dbm, key);
- if (val.dptr != NULL)
- {
- printf("key=%s, val=%s\n",key.dptr, val.dptr);
- }
- key = dbm_nextkey(dbm);
- }
- printf("end:-------------\n");
- }
- void main()
- {
- DBM *dbm = dbm_open("/Users/apple/testdb", O_RDWR | O_CREAT, 0660);
- datum key1,val1,key2,val2;
- key1.dptr = "aa";
- key1.dsize = 3;
- key2.dptr = "bb";
- key2.dsize = 3;
- val1.dptr = "vvv1";
- val1.dsize = 5;
- val2.dptr = "vvv2";
- val2.dsize = 5;
- // 添加
- if (dbm_store(dbm, key1, val1, DBM_INSERT) < 0)
- {
- printf ("insert error:%d\n", dbm_error(dbm));
- }
- if (dbm_store(dbm, key2, val2, DBM_INSERT) < 0)
- {
- printf ("insert error:%d\n", dbm_error(dbm));
- }
- traversendbm(dbm);
- // 替换
- val1.dptr = "vvv3";
- val1.dsize = 5;
- if (dbm_store(dbm, key1, val1, DBM_REPLACE) < 0)
- {
- printf ("insert error:%d\n", dbm_error(dbm));
- }
- traversendbm(dbm);
- // 删除
- int ret1 = dbm_delete(dbm, key1);
- trandbm(dbm);
- // 关闭
- dbm_close(dbm);
- }
在 iOS 系统的内部实现中所有的添加或者删除操作如果不执行 dbm_close 的话那么都不会实际保存到磁盘文件中. 因此如果你在 iOS 系统中使用这套 API 则要记得在合适的时候执行数据库关闭处理.
有一些 Unix 系统中对 key-value 的长度限制为 1024 而有些系统则没有这个限制.
欢迎大家访问欧阳大哥 2013 的 GitHub 地址和简书地址
来源: http://www.jianshu.com/p/9004893cf0e9