应用——SQLite3 C 编程学习

SQLite3 C 编程学习

文件概览

本笔记包含四个SQLite3 C语言程序,涵盖数据库基本操作、数据导入、查询和参数化查询等核心功能。

一、sqli.c - 数据库连接与简单插入

完整代码

cpp 复制代码
#include <sqlite3.h>
#include <stdio.h>

int main(int argc, char** argv)
{
    sqlite3* db = NULL;
    int ret = sqlite3_open("123.db", &db);
    if (ret != SQLITE_OK)
    {
        fprintf(stderr, "sqlite3_open %s\n", sqlite3_errmsg(db));
        sqlite3_close(db);
        return 1;
    }
    char* errmsg = NULL;
    char sql_cmd[512] = "insert into user values(6,'lvbu',31);";
    ret = sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);

    if (ret != SQLITE_OK)
    {
        fprintf(stderr, "sqlite3_exec %s\n", errmsg);
        sqlite3_free(errmsg);
        sqlite3_close(db);
        return 1;
    }
    sqlite3_close(db);
    return 0;
}

详细解释

  1. 头文件包含

    • sqlite3.h: SQLite数据库操作头文件

    • stdio.h: 标准输入输出头文件

  2. 数据库连接

    复制代码
    sqlite3* db = NULL;  // 数据库句柄指针
    int ret = sqlite3_open("123.db", &db);  // 打开数据库文件
    • sqlite3_open(): 打开或创建数据库文件

    • 返回值:SQLITE_OK表示成功,其他值为错误代码

    • 错误处理:使用sqlite3_errmsg()获取错误信息

  3. SQL语句执行

    复制代码
    char sql_cmd[512] = "insert into user values(6,'lvbu',31);";
    ret = sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);
    • sqlite3_exec(): 执行SQL语句的通用函数

    • 参数说明:

      • db: 数据库连接

      • sql_cmd: 要执行的SQL语句

      • 第3个参数:回调函数(此处为NULL)

      • 第4个参数:回调函数参数(此处为NULL)

      • &errmsg: 错误信息指针

  4. 资源清理

    • sqlite3_free(errmsg): 释放错误信息内存

    • sqlite3_close(db): 关闭数据库连接

二、sql_dict.c - 从文件导入数据到数据库

完整代码

cpp 复制代码
#include <sqlite3.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char** argv)
{
    // drop table
    // create table
    // insert
    sqlite3* db = NULL;
    int ret = sqlite3_open("123.db", &db);
    if (ret != SQLITE_OK)
    {
        fprintf(stderr, "sqlite3_open %s\n", sqlite3_errmsg(db));
        sqlite3_close(db);
        return 1;
    }
    char* errmsg = NULL;
    char sql_cmd[512] = {0};

    strcpy(sql_cmd, "drop table dict;");
    sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);

    bzero(sql_cmd, sizeof(sql_cmd));
    strcpy(sql_cmd, "create table dict(id int,word char, mean text);");
    sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);

    FILE* fp = fopen("/home/linux/dict.txt", "r");
    if (NULL == fp)
    {
        perror("fopen");
        return 1;
    }
    int i = 1;
    while (1)
    {
        char buf[1024]={0};
        if(NULL == fgets(buf,sizeof(buf),fp))
        {
            break;
        }

        char *word = strtok(buf," ");
        char* mean=strtok(NULL,"\r");
        bzero(sql_cmd,sizeof(sql_cmd));
        sprintf( sql_cmd,"insert into dict values(%d,\"%s\",\"%s\");",i++,word,mean);
        ret = sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);
        if (ret != SQLITE_OK)
        {
            fprintf(stderr, "sqlite3_exec %s , sql_cmd:%s\n", errmsg,sql_cmd);
            sqlite3_free(errmsg);
            sqlite3_close(db);
            return 1;
        }
    }
    sqlite3_close(db);
    fclose(fp);
    return 0;
}

详细解释

  1. 表格管理

    复制代码
    // 删除已存在的表
    strcpy(sql_cmd, "drop table dict;");
    sqlite3_exec(db, sql_cmd, NULL, NULL, &errmsg);
    
    // 创建新表
    bzero(sql_cmd, sizeof(sql_cmd));
    strcpy(sql_cmd, "create table dict(id int,word char, mean text);");
    • 表结构:id(整数)、word(字符)、mean(文本)
  2. 文件读取与数据解析

    复制代码
    FILE* fp = fopen("/home/linux/dict.txt", "r");
    while (1) {
        fgets(buf, sizeof(buf), fp);  // 逐行读取文件
        char *word = strtok(buf, " ");  // 用空格分隔单词和释义
        char* mean = strtok(NULL, "\r");  // 用回车符结束
    }
  3. 动态SQL语句构建

    复制代码
    sprintf(sql_cmd, "insert into dict values(%d,\"%s\",\"%s\");", i++, word, mean);
    • 注意:存在SQL注入风险(后续会讨论)
  4. 批量插入

    • 循环读取文件内容并逐条插入数据库

三、sql_select.c - 数据库查询与结果显示

完整代码

cpp 复制代码
#include <sqlite3.h>
#include <stdio.h>
// show  函数会调用多次。 次数取决于结果集,有几条记录。
//有几条记录,show就调用几次。每次result指向不同结果(记录)。
int show(void* arg, int col, char** result, char** title)
{
    static int flag = 0;
    int i = 0;
    if (0 == flag)
    {
        flag = 1;
        for (i = 0; i < col; i++)
        {
            printf("%s\t\t", title[i]);
        }
        printf("\n");
    }

    for (i = 0; i < col; i++)
    {
        printf("%s\t\t", result[i]);
    }
    printf("\n");
    return 0;
}

int main(int argc, char** argv)
{
    sqlite3* db = NULL;
    int ret = sqlite3_open("123.db", &db);
    if (ret != SQLITE_OK)
    {
        fprintf(stderr, "sqlite3_open %s\n", sqlite3_errmsg(db));
        sqlite3_close(db);
        return 1;
    }
    char* errmsg = NULL;
    char sql_cmd[512] = "select * from user ;";
    ret = sqlite3_exec(db, sql_cmd, show, NULL, &errmsg);

    if (ret != SQLITE_OK)
    {
        fprintf(stderr, "sqlite3_exec %s\n", errmsg);
        sqlite3_free(errmsg);
        sqlite3_close(db);
        return 1;
    }
    sqlite3_close(db);
    return 0;
}

详细解释

  1. 回调函数机制

    复制代码
    int show(void* arg, int col, char** result, char** title)
    • arg: 用户传递的参数(此处未使用)

    • col: 结果集的列数

    • result: 当前行的数据数组

    • title: 列名数组

  2. 静态变量使用

    复制代码
    static int flag = 0;
    • 静态变量只在第一次调用时初始化为0

    • 用于确保列标题只打印一次

  3. 查询执行

    复制代码
    ret = sqlite3_exec(db, sql_cmd, show, NULL, &errmsg);
    • 第三个参数show:查询结果的回调函数

    • 第四个参数NULL:传递给回调函数的参数

  4. 结果显示格式

    • 先打印列标题

    • 然后逐行打印数据

    • 使用制表符对齐

四、sql_arg.c - 带参数的数据库查询(存在SQL注入漏洞)

完整代码

cpp 复制代码
#include <sqlite3.h>
#include <stdio.h>
#include <string.h>
// show  函数会调用多次。 次数取决于结果集,有几条记录。
//有几条记录,show就调用几次。每次result指向不同结果(记录)。
int show(void* arg, int col, char** result, char** title)
{
    *(int*)arg = 1;
    return 0;
}

int main(int argc, char** argv)
{
    char name[50]={0};
    char pass[50]={0};
    printf("input name:");
    fgets(name,sizeof(name),stdin);
    printf("input pass:");
    fgets(pass,sizeof(pass),stdin);
    name[strlen(name)-1]='\0';
    pass[strlen(pass)-1]='\0';
    sqlite3* db = NULL;
    int ret = sqlite3_open("123.db", &db);
    if (ret != SQLITE_OK)
    {
        fprintf(stderr, "sqlite3_open %s\n", sqlite3_errmsg(db));
        sqlite3_close(db);
        return 1;
    }
    char* errmsg = NULL;
    int flag =  0;
    char sql_cmd[512] ={0};
    sprintf( sql_cmd,"select * from user where name like '%s' and age ='%s';",name,pass);
    ret = sqlite3_exec(db, sql_cmd, show, &flag, &errmsg);

    if (ret != SQLITE_OK)
    {
        fprintf(stderr, "sqlite3_exec %s\n", errmsg);
        sqlite3_free(errmsg);
        sqlite3_close(db);
        return 1;
    }
    if(0 == flag)
    {
        printf("not't find person %s \n",name);
    }
    else  
    {
        printf("name & pass correct \n");
    }

    sqlite3_close(db);
    return 0;
}

详细解释

  1. 用户输入处理

    复制代码
    fgets(name, sizeof(name), stdin);  // 安全读取输入
    name[strlen(name)-1] = '\0';      // 移除换行符
  2. 回调函数设计

    复制代码
    int show(void* arg, int col, char** result, char** title)
    {
        *(int*)arg = 1;  // 通过参数修改标志位
        return 0;
    }
    • 当查询到结果时,将flag设置为1

    • 利用参数指针在回调函数中修改主函数变量

  3. SQL注入漏洞

    复制代码
    sprintf(sql_cmd, "select * from user where name like '%s' and age ='%s';", name, pass);
    • 高危漏洞:直接拼接用户输入到SQL语句

    • 攻击示例:

      • 用户名输入:admin' --

      • 密码随意输入

      • 生成的SQL:select * from user where name like 'admin' -- ' and age ='anything';

      • --在SQL中是注释符,会注释掉后面的条件

  4. 认证逻辑

    • 通过flag判断是否找到匹配记录

    • 找到则认证成功,否则失败

安全建议

  1. 使用参数化查询(预编译语句)

    复制代码
    // 推荐使用sqlite3_prepare_v2()和sqlite3_bind_xxx()
    sqlite3_stmt *stmt;
    const char *sql = "SELECT * FROM user WHERE name = ? AND age = ?";
    sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
    sqlite3_bind_text(stmt, 1, name, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, pass, -1, SQLITE_STATIC);
  2. 输入验证

    • 验证用户输入长度和格式

    • 过滤特殊字符

  3. 错误处理改进

    • 生产环境中不应将详细错误信息返回给用户

    • 使用日志记录错误

总结

这四个程序展示了SQLite3在C语言中的基本使用:

  1. 基础操作:连接、插入、关闭

  2. 数据导入:从文件批量导入数据

  3. 数据查询:使用回调函数处理结果集

  4. 交互查询:处理用户输入(存在安全问题)

注意sql_dict.csql_arg.c中的字符串拼接方式存在SQL注入风险,在实际开发中应使用参数化查询来避免。

相关推荐
致Great2 小时前
使用 GRPO 和 OpenEnv 微调小型语言模型实现浏览器控制
数据库·人工智能·深度学习·语言模型·自然语言处理·agent·智能体
黎雁·泠崖2 小时前
C 语言预处理(下):宏与函数对比 +#/## 运算符 + 条件编译 + 头文件包含
c语言·开发语言
会思考的猴子2 小时前
UE5 笔记二 GameplayAbilitySystem Attributes & Effects
笔记·ue5
水星灭绝2 小时前
测试http下载
网络·网络协议·http
大聪明-PLUS2 小时前
在 Linux 6.8 中创建自定义系统调用
linux·嵌入式·arm·smarc
秋深枫叶红2 小时前
嵌入式第四十一篇——网络编程——udp和tcp
网络·网络协议·学习·udp
三天不学习2 小时前
【2025年CSDN博客之星主题创作文章】我在 Python 与数据智能领域的深耕与突破 —— 年度技术复盘与思考
android·数据库·python
一个平凡而乐于分享的小比特2 小时前
零拷贝技术详解:从传统IO到极致优化
linux·零拷贝
水灵龙2 小时前
文件管理自动化:.bat 脚本使用指南
java·服务器·数据库