数据库sqlite3之 sqlite3_exec()第三个参数回调函数的使用

2023-10-27

在写这篇文章之前大家先了解我之前写的关于用c语言操作sqlite3的博客,链接地址如下:
https://blog.csdn.net/makunIT/article/details/105192076
关于sqlite3_exec的回调函数的知识,我也是在做一个项目中学习到的,看了一些博客吧,很多博客,都表达的不是很清楚,所以我想写这篇博客,记录自己的学习过程。大家先了解一下sqlite3_exec()函数吧。

1、sqlite3_exec()

函数原型

#include <sqlite3.h>

int sqlite_exec(sqlite  *db, 
				const char *sql,
 				int (*callback)(void *,int,char **,char **),
				void *,
 				char **errmsg);

函数说明:用来执行sql语句,查询的结果返回给回调函数callback。
参数说明
第一个参数:db是用于保存打开的数据库文件dbname的信息;
第二个参数:sql你要执行命令的语句;
第三个参数:callback,回调函数,用来处理查询结果,如果不需要回调(比如做insert 或者delete 操作时),可以输入NULL;
第四个参数:void *是你所提供的指针,你可以传递任何一个指针参数到这里,这个参数最终会传到回调函数里面,如果不需要传递指针给回调函数,可以填NULL;
第五个参数:errmsg,返回错误信息,注意是指针的指针。

返回值:执行成功返回SQLITE_OK,否则返回其他值

说明:通常,sqlite3_callback和它后面的void*这两个位置都可以填NULL。填NULL表示你不需要回调。比如你做insert 操作,做delete操作,就没有必要使用回调。而当你做select 时,就要使用回调,因为sqlite3 把数据查出来,得通过回调告诉你查出了什么数据。虽然回调显得代码整齐,但有时候你还是想要非回调的select查询。这可以通过sqlite3_get_table 函数做到。

那么了解了以上sqlite3_exec()函数,那么我下面将讲解一下sqlite_exec()的参数回调函数callback

2、sqlite3_exec()的回调函数callback
在了解callback之前,我们先了解一下什么是回调函数:回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数;
那么在sqlite3_exec中,我们应该这样理解先执行*sql对应的功能命令,然后将结果传递给回调函数,回调函数根据结果再进一步执行。这代表着,这个 “回调函数”才是最有意义的,我们要讲我们需要的功能,通过回调函数来实现,不管是获取数据库表中有效信息,还是其他动作。

函数原型:

typedef int(*sqlite_callback)(void* para,
							 int columenCount,
 							 char** columnValue, 
 							 char** columnName);

函数说明:由用户处理查询的结果;
参数说明
第一个参数:para: 由sqlite3_exec传入的参数指针,或者说是指针参数;
第二个参数:columnCount: 查询到的这一条记录由多少个字段(多少列);
第三个参数:columnValue : 查询出来的数据都保存在这里,它实际上是个1 维数组(不要以为是2 维数组),每一个元素都是一个char * 值,是一个字段内容(用字符串来表示,以‘\0’结尾);
第四个参数:columnName : 该参数是双指针,语columnValue是对应的,表示这个字段的字段名称;
返回值:执行成功返回SQLITE_OK,否则返回其他值

说明回调函数多数时候不是执行1次,而是会循环执行n次,当我们使用select进行sql功能时,往往输出的结果会是 多行,那么 有n行,就会执行n次的 回调函数

下面我将会写两段代码,来实现回调函数,一种是直接用insert直接插入到db表中,一种是用snprintf()函数来实现向表中插入数据的。大家先来看第一种吧

第一种的代码:

/*********************************************************************************
 *      Copyright:  (C) 2020 makun<1394987689@qq.com>
 *                  All rights reserved.
 *
 *       Filename:  sqlite_delete.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(2020年03月27日)
 *         Author:  makun <1394987689@qq.com>
 *      ChangeLog:  1, Release initial version on "2020年03月27日 00时47分05秒"
 *                 
 ********************************************************************************/

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sqlite3.h>
#include <stdlib.h>

static int callback(void *data, int argc, char **argv, char **azColName);

int main (int argc, char **argv)
{
    sqlite3   *db=NULL;
    int       rc ;
    char      *zerrmsg=0;
    char      *sql;
    char      *data= "callback function called";

 //1、创建数据库,如果存在则打开
/***********************************************************************************************************************/
    rc = sqlite3_open("sqlite.db",&db);
    if(rc)
    {
        printf("create sqlite failure:%s\n",sqlite3_errmsg(db));
        exit(1);
    }

    printf("create sqlite successfuly\n");

//2、创建数据库表
/***********************************************************************************************************************/
    sql = "CREATE TABLE COMPANY("  \
                            "ID INT PRIMARY KEY     NOT NULL," \
                             "NAME          TEXT    NOT NULL," \
                             "AGE           INT     NOT NULL," \
                             "ADDRESS       CHAR(50)," \
                            "SALARY         REAL );";

    rc = sqlite3_exec(db,sql,callback, 0, &zerrmsg);
    if( rc !=SQLITE_OK)
    {
        printf("SQL error!:%s\n",zerrmsg);
        sqlite3_free(zerrmsg);
    }
    printf("create table COMPANY successfuly\n");

//3、向表中插入数据
/***********************************************************************************************************************/
    sql="INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) "  \
                             "VALUES (1, 'Paul', 32, 'California', 20000.00 ); " \
                            "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) "  \
                             "VALUES (2, 'Allen', 25, 'Texas', 15000.00 ); "     \
                             "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY)" \
                             "VALUES (3, 'Teddy', 23, 'Norway', 20000.00 );";

    rc=sqlite3_exec(db,sql,callback,(void *)data,&zerrmsg);
    if(rc !=SQLITE_OK)
    {
        printf("SQL error!:%s\n",zerrmsg);//打印错误信息
        sqlite3_free(zerrmsg);//释放掉zerrmsg的内存空间
    }
    else
    {
        printf("create company  successfuly\n");
    }

//4、查询表中的数据
/**********************************************************************************************************************/
    sql = "SELECT * from COMPANY";
    rc = sqlite3_exec(db, sql, callback, (void*)data, &zerrmsg);
    if( rc != SQLITE_OK )
    {
        printf("SQL error: %s\n", zerrmsg);
        sqlite3_free(zerrmsg);
    }
    else
    {
        printf( "Select from COMPANY successfully\n");
    }
    sqlite3_close(db);//关闭数据库
    return 0;
}
//5、查询表中的数据时用到的回调函数
/*********************************************************************************************************************/
static int callback(void *data, int argc, char **argv, char **azColName)
{
    int i;
    printf( "%s ", (const char*)data);
    for(i=0; i<argc; i++)
    {
        printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
    }
    printf("\n");
    return 0;
}
/**********************************************************************************************************************/

程序结果如下:
在这里插入图片描述

可以看出来,由于sql命令行为 select* from COMPANY,该命令会将表中所有信息都输出,总共5个字段(列),包含3条信息(行),所以这个回调函数会被执行3次,如上式结果理解这个逻辑,非常重要。

第二种代码:

/*********************************************************************************
 *      Copyright:  (C) 2020 makun<1394987689@qq.com>
 *                  All rights reserved.
 *
 *       Filename:  sqlite_insert.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(2020年03月27日)
 *         Author:  makun <1394987689@qq.com>
 *      ChangeLog:  1, Release initial version on "2020年03月27日 00时47分05秒"
 *                 
 ********************************************************************************/


#include <stdio.h>
#include <string.h>
#include <sqlite3.h>
#include <stdlib.h>
#pragma pack(1)

typedef struct tlv_data
{
    unsigned    char sn[12];
    unsigned    char temp[7];
    unsigned    char datatime[11];
    int  id;            
}tlv_send_data;
#pragma pack()

static int callback(void *ptr, int argc, char **argv, char **azCoName);
void dump_buf(char *data, int len);
//int delete_db(sqlite3 *db);

int main (int argc, char **argv)
{
    sqlite3   *db=NULL;
    int       rc = -1;
    char      *zerrmsg=0;
    char      *sql;
    char      *sql2;
    char      *sql3;
    int       rd;
    char     sn[12]={0xfd,0x01,12,52,50,49,30,30,30,31,61,0};
    char     temp[7]={0xfd,0x02,7,17,0,52,0xf9};
    char     datatime[11]={0xfd,0x03,11,78,5,13,15,1,28,0x4e,0x2a};
    tlv_send_data  read_db_t;

    dump_buf(sn, 12);//以十六进制输出
    dump_buf(temp,7);
    dump_buf(datatime,11);

//1创建数据库
/*******************************************************************************************************************/
    rc = sqlite3_open("data.db",&db);
    if(rc < 0)
    {
        printf("create data failure:%s\n",sqlite3_errmsg(db));
        return -1;
    }

    else
    {
        printf("create data successfuly\n");
    }
//2创建数据库的表格
/********************************************************************************************************************/
    if(sqlite3_exec(db,"create table if not exists temperature(id INTEGER PRIMARY KEY AUTOINCREMENT,sn varchar,temp varchar,datatime varchar);",NULL,NULL,&zerrmsg)!=SQLITE_OK)
    {
        printf("Create failed:%s\n",zerrmsg);
        return -1;
    }

    printf("Open table temperature successfully!\n");
//3向表格中插入数据
/************************************************************************************************************************/

    char insert[1024];
    memset(insert,0, sizeof(insert));
    //将三个数组中的字节存放到数据库中,以字符串格式存储
    snprintf(insert,sizeof(insert),"insert into temperature values(null,'%s','%s','%s');",sn,temp,datatime);
    if((sqlite3_exec(db, insert, NULL, NULL,&zerrmsg) != SQLITE_OK ))
    {   
        printf("sql error:%s\n",zerrmsg);
        sqlite3_free(zerrmsg);
    }   


    printf("Insert successfuly\n");
//4读取数据库中表中的数据 ,只读取数据表中一行数据
/************************************************************************************************************************/
    memset(&read_db_t,0,sizeof(read_db_t));
    read_db(db, &read_db_t);
   // delete_db(db);
    return 0;
}

//5读取数据库中表中的数据,只读取表中一行数据
/***********************************************************************************************************/
int  read_db(sqlite3 *db,tlv_send_data *pp)
{

    char *zErrmsg=NULL;
    int   rc; 
    char *sql2="select *from temperature limit 1 ";

    rc=sqlite3_exec(db, sql2,callback, (void *)pp,&zErrmsg);
    if(rc !=SQLITE_OK)
    {   
        printf("sql error:%s\n", zErrmsg);
        sqlite3_free(zErrmsg);
    }   
    else
    {   
        printf("select from temperature successfuly\n");
    }   

    return  0;  
}
//6读取数据时用到的回调函数
/********************************************************************************************************************/
static int callback(void *ptr, int argc, char **argv, char **azCoName)
{
    int i;

    tlv_send_data *data =(tlv_send_data *)ptr;//将ptr强制转换为tlv_send_data,然后存放获取第一条数据的信息,然后返回给参数pp

    for(i=0;i <argc; i++)
    {
        printf("%s = %s\n",azCoName[i],argv[i]?argv[i]:"NULL" );
        if(!strcmp("id",azCoName[i]))
        {
            data->id=atoi(argv[i]);
        }
        else if(!strcmp("sn",azCoName[i]))
        {
            strcpy(data->sn,argv[i]);
            dump_buf(data->sn,12);
        }
        else if(!strcmp("temp",azCoName[i]))
        {
            strcpy(data->temp,argv[i]);
            dump_buf(data->temp,7);

        }
        else if(!strcmp("datatime",azCoName[i]))
        {
            strcpy(data->datatime,argv[i]);
            dump_buf(data->datatime,11);
        }
    }
        printf("\n");

        return 0;
 
}
//以十六进制输出数组中的数据
/************************************************************************************************************************/
void dump_buf( char *data, int len)//data指针,指向buf的首地址,len是buf的长度
{
    int i;
    for(i=0; i<len; i++)
    { 
        printf("0x%02x ",(unsigned char)data[i]);
        if( 0 == (i+1)%16 )
         printf("\n");
    }   
        printf("\n");
}
/**********************************************************************************************************************/

/*int delete_db(sqlite3 *db)
{
    char *zErrmsg =NULL;
    char *sql3="DELETE FROM temperature where id =(SELECT id from temperature limit 1)";

    if((sqlite3_exec(db, sql3, NULL, NULL,&zErrmsg) != SQLITE_OK ))
    {   
        printf("sql error:%s\n",zErrmsg);
        sqlite3_free(zErrmsg);
    }  
    printf("delect from temperature successfuly\n");
    return 0;
}*/

在这里插入图片描述
分析:在上式中我将三个数组中的字节按照%s的格式存放在数据库中,当我用回调函数读取数据库中的数据时,我们会发现,图片中打印的是乱码,printf函数以参数"%s"输出字符串时过程为:
(1)从首地址开始逐字节寻址,把存储单元(一个字节)内的数据转换为ASCII字符格式输出。
(2)直到某一个字节内存的元素为字符’\0’时,输出此字符并且寻址结束。
字符数组里没有’\0’,因此使用printf %s 输出时,可能会因为没有结束的’\0’而多输出一些乱码或是字符串。如果要正确的输出字符数组,最好一位一位输出。
另外根据上面的情况我们知道,callback函数,第三个参数:columnValue : 查询出来的数据都保存在这里,它实际上是个1 维数组(不要以为是2 维数组),每一个元素都是一个char * 值,是一个字段内容(用字符串来表示,以‘\0’结尾);
而我们在定义temp时temp[4]=0,temp[5]=52,temp[6]=f9但是当我们用回调函数打印时我们会惊奇的发现的temp[5]=0;temp[6]=0,这个数据出现的问题就是callback的第三个参数引起的,它将查询的数据用char *保存的,(用字符串来表示,以’\0‘结尾),我在做项目的时候就发现了这一点,刚开始看的时候关于这个细节没有把握住,以至于花了自己一点时间,但是对于犯错,我们并不可怕,可怕的是遇到错误我们就放弃了。

在这里插入图片描述在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据库sqlite3之 sqlite3_exec()第三个参数回调函数的使用 的相关文章

  • 【计算机开题报告】 网上茶叶销售平台设计与开发

    一 选题依据 简述国内外研究现状 生产需求状况 说明选题目的 意义 列出主要参考文献 1 研究背景 随着社会经济的迅速发展和科学技术的全面进步 以计算机与网络技术为基础的信息系统正处于蓬勃发展的时期 随着经济文化水平的提高 近年来 随着科学
  • MySQL中设置自增主键id从1开始

    可能遇到过这种问题 当你只想新增一条数据时 发现使用Insert语句后 发现id并不是从1开始的 握草 怎么回事 其实很简单 通过执行一下SQL 对应你的表就可以解决 ALTER TABLE user AUTO INCREMENT 1 具体
  • 【计算机开题报告】基于JSP的服装店销售管理系统

    1 选课目的意义 21世纪是一个信息化时代 随着中国经济的发展和人民生活水平的提高 服装商场的普及程度日益增大 竞争也在逐渐白炽化 为了进一步提高服装商场的经营效率 在服装店销售管理中引入计算机管理系统成为了必然的选择 由于中国环境的特殊性
  • 【计算机开题报告】 医药信息管理系统

    一 选题依据 简述国内外研究现状 生产需求状况 说明选题目的 意义 列出主要参考文献 1 研究背景 随着医药事业的不断壮大 相关单位对于医药信息的管理变得越来越重要 传统的手工管理效率低 易出错 费时费力 不能及时精确的收集 传递 存储 加
  • 【计算机开题报告】图书管理系统

    一 选题依据 简述国内外研究现状 生产需求状况 说明选题目的 意义 列出主要参考文献 国内外研究现状 国外研究现状 在很多发达国家 图书管理系统的应用和技术发展已经相对完善 并且还建立了数字图书馆 各方面的情况也非常成熟 而图书管理的应用价
  • Qt源码分析:Qt程序是怎么运行起来的?

    一 从 exec 谈起 一个标准的Qt gui程序 在启动时我们会coding如下几行简洁的代码 include widget h include
  • 软件开发和网络安全哪个更好找工作?

    为什么今年应届毕业生找工作这么难 有时间去看看张雪峰今年为什么这么火就明白了 这么多年人才供给和需求错配的问题 在经济下行的今年 集中爆发 供给端 大学生越来越多 需求端 低端工作大家不愿去 高端岗位又太少 很多基础行业 比如机械 土木 所
  • AntDB内存管理之内存上下文之内存上下文机制是怎么实现的

    4 内存上下文机制是怎么实现的 下文将针对内存上下文机制进行代码说明 本次以AntDB的代码为例 来解析内存上下文的实现方式 4 1 最基础的数据结构 MemoryContextData和MemoryContextMethods是内存上下文
  • 亚信安慧AntDB引领数字化转型:浙江移动成功实现CRM系统全域改造

    数字时代 通信运营商在不断迭代的背景下 需要不断探索数字化转型的路径 以适应快速发展的市场和技术环境 在这一浪潮中 浙江移动站在前沿 率先完成了其CRM系统的全域改造 采用了亚信安慧公司研发的AntDB数据库 为整个行业树立了数字化转型的标
  • 智能时代:自然语言生成SQL与知识图谱问答实战

    语义解析 前言 语义解析的应用场景 总结概论 语义解析和大模型的关系 延伸阅读 前言 语义解析技术可以提高人机交互的效率和准确性 在自然语言处理 数据分析 智能客服 智能家居等领域都有广泛的应用前景 特别是在大数据时代 语义解析能够帮助企业
  • 【计算机毕业设计】出租车管理系统

    现代经济快节奏发展以及不断完善升级的信息化技术 让传统数据信息的管理升级为软件存储 归纳 集中处理数据信息的管理方式 本出租车管理系统就是在这样的大环境下诞生 其可以帮助管理者在短时间内处理完毕庞大的数据信息 使用这种软件工具可以帮助管理人
  • 【计算机毕业设计】网上拍卖系统

    现代经济快节奏发展以及不断完善升级的信息化技术 让传统数据信息的管理升级为软件存储 归纳 集中处理数据信息的管理方式 本网上拍卖系统就是在这样的大环境下诞生 其可以帮助使用者在短时间内处理完毕庞大的数据信息 使用这种软件工具可以帮助管理人员
  • 【计算机毕业设计】Java图书馆智能选座系统

    现代经济快节奏发展以及不断完善升级的信息化技术 让传统数据信息的管理升级为软件存储 归纳 集中处理数据信息的管理方式 本图书馆智能选座系统就是在这样的大环境下诞生 其可以帮助使用者在短时间内处理完毕庞大的数据信息 使用这种软件工具可以帮助管
  • c语言学生管理系统

    创建结构体里面包含学生的各种信息 struct xs int xh char xm 20 int gs yy wl double pj struct xs next 创建菜单 void menu printf n n printf 学生管理
  • 华为OD机试真题-API集群负载统计-Java-OD统一考试(C卷)

    题目描述 某个产品的RESTful API集合部署在服务器集群的多个节点上 近期对客户端访问日志进行了采集 需要统计各个API的访问频次 根据热点信息在服务器节点之间做负载均衡 现在需要实现热点信息统计查询功能 RESTful API的由多
  • 深入了解 Python MongoDB 操作:排序、删除、更新、结果限制全面解析

    Python MongoDB 排序 对结果进行排序 使用 sort 方法对结果进行升序或降序排序 sort 方法接受一个参数用于 字段名 一个参数用于 方向 升序是默认方向 示例 按名称按字母顺序对结果进行排序 import pymongo
  • 做测试不会 SQL?超详细的 SQL 查询语法教程来啦!

    前言 作为一名测试工程师 工作中在对测试结果进行数据比对的时候 或多或少要和数据库打交道的 要和数据库打交道 那么一些常用的sql查询语法必须要掌握 最近有部分做测试小伙伴表示sql查询不太会 问我有没有sql查询语法这一块的文档可以学习
  • 面试官问,如何在十亿级别用户中检查用户名是否存在?

    面试官问 如何在十亿级别用户中检查用户名是否存在 前言 不知道大家有没有留意过 在使用一些app注册的时候 提示你用户名已经被占用了 需要更换一个 这是如何实现的呢 你可能想这不是很简单吗 去数据库里查一下有没有不就行了吗 那么假如用户数量
  • 温室气体排放更敏感的模型(即更高的平衡气候敏感性(ECS))在数年到数十年时间尺度上也具有更高的温度变化(Python代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Python代码 数据
  • SAP ERP系统是什么?SAP好用吗?

    A公司是一家传统制造企业 公司曾先后使用过数个管理软件系统 但各部门使用的软件都是单独功能 导致企业日常管理中数据流与信息流相对独立 形成了 信息孤岛 随着公司近年业务规模的快速发展以及客户数量的迅速增加 企业原有的信息系统在销售预测及生产

随机推荐