day40 SQLite3單詞查詢程序設計與實現
核心知識點
- SQLite3 C接口應用:使用
sqlite3_open
、sqlite3_exec
等函數操作數據庫 - 回調函數機制:通過回調函數處理查詢結果集
- SQL語句構建:動態生成
SELECT
、INSERT
等SQL語句 - 事務處理:使用
BEGIN TRANSACTION
和COMMIT
提高批量插入效率 - 結果集處理:理解
result
數組與查詢列的對應關系 - 用戶交互設計:實現連續查詢和退出機制
完整代碼實現(帶詳細注釋)
#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查詢語句的精準投影
sprintf(sql_cmd, "select dict_mean from dict where word like \"%s\"", want_word);
select dict_mean
:這是關鍵的"投影"操作,明確指定只返回dict_mean
這一列- 結果集結構:當查詢命中時,返回的結果集每行只有1列數據
- 數組索引關系:在回調函數中,
result[0]
自然對應這唯一一列(即單詞釋義)
2. 數據存儲結構的一致性
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
標志判斷是否找到
代碼執行流程與理想結果
初始化階段(首次運行)
$ 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接口應用的典型最佳實踐。