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接口应用的典型最佳实践。

相关推荐
闲人编程2 天前
构建一个短链接生成器服务(FastAPI + SQLite)
jvm·python·sqlite·fastapi·生成器·短链接·caodecapsule
Jay_Franklin2 天前
Python中使用sqlite3模块和panel完成SQLite数据库中PDF的写入和读取
数据库·笔记·python·pycharm·sqlite·pdf·py
l1t3 天前
利用DeepSeek改写SQLite版本的二进制位数独求解SQL
数据库·人工智能·sql·sqlite
l1t4 天前
利用DeepSeek优化SQLite求解数独SQL用于DuckDB
开发语言·数据库·sql·sqlite·duckdb
国服第二切图仔5 天前
Rust开发实战之操作SQLite数据库——从零构建数据持久化应用
数据库·rust·sqlite
l1t5 天前
编译SQLite 3.51源码并体验新功能
单元测试·sqlite·duckdb
不剪发的Tony老师5 天前
SQLite 3.51.0发布,新功能解读
数据库·sqlite
【D'accumulation】6 天前
.NET Framework 4.8 + Microsoft.Data.Sqlite 报 Library e_sqlite3 not found
microsoft·sqlite·.net
小灰灰搞电子6 天前
Rust 操作Sqlite数据库详细教程
数据库·rust·sqlite