day40 SQLite3单词查询程序设计与实现
核心知识点
- SQLite3 C接口应用 :使用
sqlite3_open
、sqlite3_exec
等函数操作数据库 - 回调函数机制:通过回调函数处理查询结果集
- SQL语句构建 :动态生成
SELECT
、INSERT
等SQL语句 - 事务处理 :使用
BEGIN TRANSACTION
和COMMIT
提高批量插入效率 - 结果集处理 :理解
result
数组与查询列的对应关系 - 用户交互设计:实现连续查询和退出机制
完整代码实现(带详细注释)
c
#include <sqlite3.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// SQLite查询结果回调函数:用于处理查询到的结果
// 参数说明:
// arg:传递给回调的用户数据指针(此处用于传递"是否找到"的标志)
// col:查询结果的列数(本例中固定为1列)
// result:查询结果数据数组(每行结果的值)
// title:查询结果的列名数组(本例中为["dict_mean"])
int find(void* arg, int col, char** result, char** title)
{
// 将找到结果的标志设为1(表示已找到匹配数据)
*(int*)arg = 1;
// 打印查询到的单词释义(result[0]对应SELECT指定的dict_mean列)
printf("mean:%s\n", result[0]);
return 0; // 回调函数返回0表示继续处理其他结果
}
int main(int argc, char** argv)
{
sqlite3* db = NULL; // SQLite数据库连接句柄
char* errmsg = NULL; // 用于存储SQL操作错误信息
int ret = 0; // 用于存储函数调用返回值
// 打开指定的SQLite数据库(如果不存在则创建)
ret = sqlite3_open("./aaa.db", &db);
if (SQLITE_OK != ret)
{
// 打开数据库失败,打印错误信息
fprintf(stderr, " sqlite3_open %s\n", sqlite3_errstr(ret));
sqlite3_close(db); // 关闭数据库连接
return 1; // 程序异常退出
}
char sql_cmd[1024] = {0}; // 用于存储SQL命令字符串
// 尝试删除已存在的dict表(如果存在) - 清理旧数据
strcpy(sql_cmd, "drop table dict");
sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);
// 注意:删除失败可能是表不存在,属于正常情况
// 创建新的dict表,包含id(序号)、word(单词)、dict_mean(释义)三个字段
bzero(sql_cmd, sizeof(sql_cmd)); // 清空SQL命令缓冲区
strcpy(sql_cmd, "create table dict(id int ,word char ,dict_mean text);");
ret = sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);
if (SQLITE_OK != ret)
{
// 创建表失败,打印错误信息
fprintf(stderr, " sqlite3_exec sql_cmd:[%s] %s\n", sql_cmd, errmsg);
sqlite3_free(errmsg); // 释放错误信息内存
sqlite3_close(db); // 关闭数据库连接
return 1; // 程序异常退出
}
// 打开单词词典文件(路径为/home/linux/dict.txt)
FILE* fp = fopen("/home/linux/dict.txt", "r");
if (NULL == fp)
{
perror("fopen"); // 打开文件失败,打印错误信息
return 1; // 程序异常退出
}
int num = 1; // 用于记录单词的序号(id字段)
// 开始数据库事务(批量插入时使用事务可提高效率)
bzero(sql_cmd, sizeof(sql_cmd));
strcpy(sql_cmd, "begin transaction;");
sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);
// 循环读取词典文件内容并插入到数据库中
while (1)
{
char linebuf[1024] = {0}; // 用于存储读取到的一行数据
// 读取文件中的一行数据,如果读取失败(到文件末尾)则退出循环
if (NULL == fgets(linebuf, sizeof(linebuf), fp))
{
break;
}
// 解析行数据:以空格分割单词和释义
char* word = strtok(linebuf, " "); // 获取单词部分
char* mean = strtok(NULL, "\r"); // 获取释义部分(去除回车符)
// 构造插入数据的SQL命令
bzero(sql_cmd, sizeof(sql_cmd));
sprintf(sql_cmd, "insert into dict values(%d,\"%s\",\"%s\");", num++, word, mean);
// 执行插入操作
ret = sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);
if (SQLITE_OK != ret)
{
// 插入失败,打印错误信息
fprintf(stderr, " sqlite3_exec sql_cmd:[%s] %s\n", sql_cmd, errmsg);
sqlite3_free(errmsg); // 释放错误信息内存
sqlite3_close(db); // 关闭数据库连接
return 1; // 程序异常退出
}
}
// 提交事务(将之前的批量插入操作真正写入数据库)
bzero(sql_cmd, sizeof(sql_cmd));
strcpy(sql_cmd, "commit;");
sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);
// 循环接收用户输入,查询单词释义
while (1)
{
char want_word[1024] = {0}; // 用于存储用户要查询的单词
printf("input word:"); // 提示用户输入单词
fgets(want_word, sizeof(want_word), stdin); // 读取用户输入
want_word[strlen(want_word) - 1] = '\0'; // 去除输入中的换行符
// 如果用户输入#quit,则退出查询循环
if (0 == strcmp(want_word, "#quit"))
{
break;
}
// 构造查询单词释义的SQL命令(仅选择dict_mean列)
bzero(sql_cmd, sizeof(sql_cmd));
sprintf(sql_cmd, "select dict_mean from dict where word like \"%s\"", want_word);
int flag = 0; // 用于标记是否查询到结果(0:未找到,1:找到)
// 执行查询操作,使用find函数作为结果回调函数
ret = sqlite3_exec(db, sql_cmd, find, &flag, &errmsg);
if (SQLITE_OK != ret)
{
// 查询失败,打印错误信息
fprintf(stderr, " sqlite3_exec sql_cmd:[%s] %s\n", sql_cmd, errmsg);
sqlite3_free(errmsg); // 释放错误信息内存
sqlite3_close(db); // 关闭数据库连接
return 1; // 程序异常退出
}
// 如果未找到匹配的单词,提示用户
if (0 == flag)
{
printf("cant find %s\n", want_word);
}
}
// 关闭数据库连接
sqlite3_close(db);
return 0; // 程序正常退出
}
关键机制解析:result[0]
为何精确对应单词释义
1. SQL查询语句的精准投影
c
sprintf(sql_cmd, "select dict_mean from dict where word like \"%s\"", want_word);
select dict_mean
:这是关键的"投影"操作,明确指定只返回dict_mean
这一列- 结果集结构 :当查询命中时,返回的结果集每行只有1列数据
- 数组索引关系 :在回调函数中,
result[0]
自然对应这唯一一列(即单词释义)
2. 数据存储结构的一致性
c
sprintf(sql_cmd, "insert into dict values(%d,\"%s\",\"%s\");", num++, word, mean);
- 插入顺序固定 :第二列存储单词(
word
),第三列存储释义(dict_mean
) - 查询匹配 :当
where word like "%s"
条件满足时,返回的正是第三列存储的释义数据
3. 回调函数执行机制
查询结果情况 | 回调函数调用 | result 数组内容 |
flag 值 |
---|---|---|---|
找到1个匹配结果 | 调用1次 | result[0] = 释义 |
1 |
找到N个匹配结果 | 调用N次 | 每次result[0] 不同 |
1 |
无匹配结果 | 不调用 | - | 0 |
重要特性 :当查询无结果时,回调函数不会被调用 ,因此需通过
flag
标志判断是否找到
代码执行流程与理想结果
初始化阶段(首次运行)
shell
$ gcc word_query.c -lsqlite3 -o word_query
$ ./word_query
理想输出:
input word:hello
mean:你好
input word:world
mean:世界
input word:apple
mean:苹果
input word:banana
mean:香蕉
input word:#quit
查询场景模拟
场景1:成功查询单词
input word:computer
mean:计算机
- 执行过程 :
- 构造SQL:
select dict_mean from dict where word like "computer"
- 数据库返回匹配行
- 触发回调函数:
find()
设置flag=1
并打印释义
- 构造SQL:
场景2:未找到单词
input word:zxy123
cant find zxy123
- 执行过程 :
- 构造SQL:
select dict_mean from dict where word like "zxy123"
- 数据库无匹配结果
- 不触发回调函数
- 检测
flag=0
,输出"cant find"提示
- 构造SQL:
场景3:退出程序
input word:#quit
- 执行过程 :
- 检测到输入
#quit
- 跳出查询循环
- 执行
sqlite3_close(db)
关闭数据库 - 程序正常退出(返回0)
- 检测到输入
核心结论
result[0]
能精确获取单词释义,是由双重保障决定的:
- SQL投影约束 :
SELECT dict_mean
确保结果集仅包含释义列 - 数据存储一致性:插入时严格保证第三列存储释义数据
这种设计使回调函数能直接通过result[0]
获取目标数据,无需处理列索引映射问题,是SQLite C接口应用的典型最佳实践。