day40 SQLite3单词查询程序设计与实现

day40 SQLite3单词查询程序设计与实现

核心知识点

  • SQLite3 C接口应用 :使用sqlite3_opensqlite3_exec等函数操作数据库
  • 回调函数机制:通过回调函数处理查询结果集
  • SQL语句构建 :动态生成SELECTINSERT等SQL语句
  • 事务处理 :使用BEGIN TRANSACTIONCOMMIT提高批量插入效率
  • 结果集处理 :理解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:计算机
  • 执行过程
    1. 构造SQL:select dict_mean from dict where word like "computer"
    2. 数据库返回匹配行
    3. 触发回调函数:find()设置flag=1并打印释义
场景2:未找到单词
复制代码
input word:zxy123
cant find zxy123
  • 执行过程
    1. 构造SQL:select dict_mean from dict where word like "zxy123"
    2. 数据库无匹配结果
    3. 不触发回调函数
    4. 检测flag=0,输出"cant find"提示
场景3:退出程序
复制代码
input word:#quit
  • 执行过程
    1. 检测到输入#quit
    2. 跳出查询循环
    3. 执行sqlite3_close(db)关闭数据库
    4. 程序正常退出(返回0)

核心结论

result[0]能精确获取单词释义,是由双重保障决定的:

  1. SQL投影约束SELECT dict_mean确保结果集仅包含释义列
  2. 数据存储一致性:插入时严格保证第三列存储释义数据

这种设计使回调函数能直接通过result[0]获取目标数据,无需处理列索引映射问题,是SQLite C接口应用的典型最佳实践。

相关推荐
小马学嵌入式~13 小时前
嵌入式 SQLite 数据库开发笔记
linux·c语言·数据库·笔记·sql·学习·sqlite
言之。17 小时前
Django REST Framework 中 @action 装饰器详解
数据库·sqlite
yddddddy1 天前
SQLite的基本操作
数据库·sqlite
cellurw1 天前
Day39 SQLite数据库操作与HTML核心API及页面构建
数据库·sqlite·html
言之。1 天前
Django高效查询:values_list实战详解
django·sqlite·list
程序员如山石1 天前
sqlite3的加解密全过程
sqlite
言之。1 天前
Django中的软删除
数据库·django·sqlite
Mr_Xuhhh2 天前
sqlite3的使用
jvm·oracle·sqlite
dreams_dream2 天前
企业级 Django 日志配置示例
数据库·django·sqlite