SQLite数据库C_C++接口(保姆级API应用 1.4W字)(全网最详细介绍,学完必掌握)

news2024/11/23 11:26:44

目录

sqlite3的C/C++ API应用

前言

SQLite3库安装

API函数

打开、关闭、错误处理

打开

返回值

关闭

错误调试

实际应用

执行SQL(DDL、DML)

API介绍

实际应用

回调函数查询

API介绍

实际应用

全缓冲查询

API介绍

实际应用

字节缓冲查询

API介绍

实际应用

三种查询方式总结

sqlite实现C语言自定义函数封装


sqlite3的C/C++ API应用

前言

对于主键和外键知识点的补充:

  • 主键的值不能重复,一般将自增的字段设置为主键。
    • 主键是用来唯一表示一条数据的值,不能重复的。比如,一条记录包括身份正号,姓名,年龄。身份证号是唯一能确定你这个人的,其他都可能有重复,所以,身份证号薯磨是主键。
  • 外键主要目的是控制存储在外键表中的数据。 使两张表形成关联,外键只能引用外表中的列的值或使用空值。
    • 外键用于与另一张表的关联。是能确定另一张表记录的字段,用于保持数据的一致性。比如,A表中的一个字段,是B表的主键,那他就可以是A表的外键。

SQLite3库安装

在 C/C++ 程序中使用 SQLite 之前,我们需要确保机器上已经有 SQLite 库,这个库提供了C/C++的操作SQLite的编程接口API。

  • 在linux下只需输入安装命令:sudo apt-get install libsqlite3-dev
  • QT下使用SQLite3数据库

https://www.cnblogs.com/tfanalysis/p/4073756.html

  • Windows下:https://www.cnblogs.com/White-strategy-group/p/6360003.html

我使用的是Windows下通过vscode远程SSH访问Linux,因此需要在windows下也安装并添加相应的库文件。

安装前,我们在vscode中添加SQLite的头文件,提示找不到头文件

因此我们需要先定位头文件包含路径

然后到sqlite官网下载源码包(sqlite3的源码)

https://www.cnblogs.com/White-strategy-group/p/6360003.html

解压缩后文件内容如图所示

将三个.h文件添加到之前的includePath中:"D:/myinclude/**"

我们先在目录下新建一个sqlite文件夹

然后将头文件添加进来

如果发现添加完之后仍然自动找不到头文件,如下所示

我们需要手动包含具体头文件路径,如:"D:\\myinclude\\sqlite3"

我们输入sqlite3发现,可以自动提示补全,则表示库文件添加成功!

API函数

打开、关闭、错误处理

打开

第一个参数为指定要打开的数据库的名字,也包括数据库的路径

第二个参数为一个二级指针,这里的作用相当于文件描述符,它是一个数据库文件指针。传入的是一个一级指针的地址作为输出,给指针的具体指向赋值(定义一个空指针传入过来,最终会给这个指针赋值)(通过操作数据库文件指针就相对于操作数据库)

返回值

SQLite3的C/C++接口函数所有返回值如下:

#define SQLITE_OK 0 /* Successful result */
#define SQLITE_ERROR 1 /* SQL error or missing database */
#define SQLITE_INTERNAL 2 /* An internal logic error in SQLite */
#define SQLITE_PERM 3 /* Access permission denied */
#define SQLITE_ABORT 4 /* Callback routine requested an abort */
#define SQLITE_BUSY 5 /* The database file is locked */
#define SQLITE_LOCKED 6 /* A table in the database is locked */
#define SQLITE_NOMEM 7 /* A malloc() failed */
#define SQLITE_READONLY 8 /* Attempt to write a readonly database */
#define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite_interrupt() */
#define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */
#define SQLITE_CORRUPT 11 /* The database disk image is malformed */
#define SQLITE_NOTFOUND 12 /* (Internal Only) Table or record not found */
#define SQLITE_FULL 13 /* Insertion failed because database is full */
#define SQLITE_CANTOPEN 14 /* Unable to open the database file */
#define SQLITE_PROTOCOL 15 /* Database lock protocol error */
#define SQLITE_EMPTY 16 /* (Internal Only) Database table is empty */
#define SQLITE_SCHEMA 17 /* The database schema changed */
#define SQLITE_TOOBIG 18 /* Too much data for one row of a table */
#define SQLITE_CONSTRAINT 19 /* Abort due to contraint violation */
#define SQLITE_MISMATCH 20 /* Data type mismatch */
#define SQLITE_MISUSE 21 /* Library used incorrectly */
#define SQLITE_NOLFS 22 /* Uses OS features not supported on host */
#define SQLITE_AUTH 23 /* Authorization denied */
#define SQLITE_ROW 100 /* sqlite_step() has another row ready */
#define SQLITE_DONE 101 /* sqlite_step() has finished executing */

关闭

错误调试

返回错误信息

返回值错误码

实际应用

#include <stdio.h>
#include <sqlite3.h>

int main(int argc, char const *argv[])
{
    sqlite3 *db;
    int ret = sqlite3_open(argv[1],&db);

    if(ret != SQLITE_OK)
    {
        printf("sqlite3 open:%s\n",sqlite3_errmsg(db));
        exit(-1);
    }

    printf("sqlite open db successfully!\n");

    sqlite3_close(db);
    return 0;
}

编译的时候,不能直接编译

我们需要像使用POSIX库一样,手动链接sqlite3库

运行结果:

但是该函数有个bug,即使不传任何参数,也不会报错

因此最好添加一个命令行传参判断

#include <stdio.h>
#include <sqlite3.h>

int main(int argc, char const *argv[])
{
    if(argc != 2)
    {
        printf("Please input db name!\n");
        exit(-1);
    }

    sqlite3 *db;
    int ret = sqlite3_open(argv[1],&db);

    if(ret != SQLITE_OK)
    {
        printf("sqlite3 open:%s\n",sqlite3_errmsg(db));
        exit(-1);
    }

    printf("sqlite open db successfully!\n");

    sqlite3_close(db);
    return 0;
}

执行SQL(DDL、DML)

API介绍

执行SQL语句函数,该函数一共有5个参数

参数1:数据库文件句柄

参数2:要执行的SQL语句

参数3:回调函数,传入一个函数指针(这里的sqlite_callback callback中的sqlite_callback是通过函数指针重命名的,如下图所示)。注:回调函数只对SQL查询语句有效。当指定的是一个非查询操作,该参数应该置为NULL,否则即使传入了回调函数,回调函数也不会被执行。

参数4:回调函数的参数

参数5:保存执行SQL后的错误信息,传入的是一级指针的地址

实际应用

我们首先创建一个学生表,其中我们对错误检查进行了二次封装

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

void print_error(int ret, char *err, sqlite3 *db)
{
    if(ret != SQLITE_OK)
    {
        printf("%s:%s\n",err,sqlite3_errmsg(db));
        exit(-1);
    }
}

int main(int argc, char const *argv[])
{
    if(argc != 2)
    {
        printf("Please input db name!\n");
        exit(-1);
    }

    sqlite3 *db;
    char *errmsg;
    char sql[1024] = {0};
    int ret = sqlite3_open(argv[1],&db);

    print_error(ret,"sqlite open",db);

    printf("sqlite open db successfully!\n");

    strcpy(sql,"create table student(id integer primary key, name text, age integer)");
    
    ret = sqlite3_exec(db,sql,NULL,NULL,&errmsg);

    print_error(ret,"sqlite exec create table",db);

    printf("create table successfully\n");

    sqlite3_close(db);
    return 0;
}

然后通过键盘输入的方式往表里插入三条数据

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

void print_error(int ret, char *err, sqlite3 *db)
{
    if(ret != SQLITE_OK)
    {
        printf("%s:%s\n",err,sqlite3_errmsg(db));
        exit(-1);
    }
}

int main(int argc, char const *argv[])
{
    if(argc != 2)
    {
        printf("Please input db name!\n");
        exit(-1);
    }

    sqlite3 *db;
    char *errmsg;
    char sql[1024] = {0};

    int id;
    char name[20];
    int age;

    int ret = sqlite3_open(argv[1],&db);

    print_error(ret,"sqlite open",db);

    printf("sqlite open db successfully!\n");

    strcpy(sql,"create table if not exists student(id integer primary key, name text, age integer)");
    
    ret = sqlite3_exec(db,sql,NULL,NULL,&errmsg);

    print_error(ret,"sqlite exec create table",db);

    printf("create table successfully\n");

    //插入3行数据:id,name,age 键盘输入
    for(int i=0;i<3;i++)
    {
        printf("Please input id:\n");
        scanf("%d",&id);

        printf("Please input name:\n");
        scanf("%s",name);

        printf("Please input age:\n");
        scanf("%d",&age);

        //sql:insert into student(id,name,age)values();
        //sprint();写入到字符串 fprintf();写入到文件
        memset(sql,0,sizeof(sql));
        sprintf(sql,"insert into student(id,name,age)values(%d,'%s',%d)",id,name,age);
        ret = sqlite3_exec(db,sql,NULL,NULL,&errmsg);
        print_error(ret,"sqlite exec create table",db);
    }

    sqlite3_close(db);
    return 0;
}

如果我们想要删除zhangsan的数据,可以使用如下

#include <stdio.h>
#include <sqlite3.h>
#include <string.h>
#include <stdlib.h>
#define DELETE_DATA 1
#define INSERT_DATA 0
void print_error(int ret, char *err, sqlite3 *db)
{
    if(ret != SQLITE_OK)
    {
        printf("%s:%s\n",err,sqlite3_errmsg(db));
        exit(-1);
    }
}

int main(int argc, char const *argv[])
{
    if(argc != 2)
    {
        printf("Please input db name!\n");
        exit(-1);
    }

    sqlite3 *db;
    char *errmsg;
    char sql[1024] = {0};

    int id;
    char name[20];
    int age;

    int ret = sqlite3_open(argv[1],&db);

    print_error(ret,"sqlite open",db);

    printf("sqlite open db successfully!\n");

    strcpy(sql,"create table if not exists student(id integer primary key, name text, age integer)");
    
    ret = sqlite3_exec(db,sql,NULL,NULL,&errmsg);

    print_error(ret,"sqlite exec create table",db);

    printf("create table successfully\n");

#if INSERT_DATA
    //插入3行数据:id,name,age 键盘输入
    for(int i=0;i<3;i++)
    {
        printf("Please input id:\n");
        scanf("%d",&id);

        printf("Please input name:\n");
        scanf("%s",name);

        printf("Please input age:\n");
        scanf("%d",&age);

        //sql:insert into student(id,name,age)values();
        //sprint();写入到字符串 fprintf();写入到文件
        memset(sql,0,sizeof(sql));
        sprintf(sql,"insert into student(id,name,age)values(%d,'%s',%d)",id,name,age);
        ret = sqlite3_exec(db,sql,NULL,NULL,&errmsg);
        print_error(ret,"sqlite exec create table",db);
    }
#endif

#if DELETE_DATA
    printf("Please input who do you want to delete:\n");
    memset(sql,0,sizeof(sql));
    memset(name,0,sizeof(name));
    scanf("%s",name);
    sprintf(sql,"delete from student where name = '%s'",name);
    ret = sqlite3_exec(db,sql,NULL,NULL,&errmsg);
    print_error(ret,"sqlite exec delete data",db);
#endif
    sqlite3_close(db);
    return 0;
}

注:SQLite库提供的是原生态的接口,我们可以对其进行二次封装,像之前错误检查那样,对sqlite3_exec要执行的SQL语句,进行封装,避免代码的冗余。

回调函数查询

API介绍

每查询到一条结果就会回调一次这个函数,通过一行行缓冲数据,每次缓冲一行。

参数1:传入的参数

参数2:保存查询到的结果每一行中列的个数

参数3:保存查询到数据中每一列的值,用一个指针数组(保存指针的数组,本质是数组)来接

参数4:保存每一列的字段名字,用一个指针数组来接

实际应用

以打印查询到的结果每行列数为例:如果回调函数不加return 0;,那么将只执行一次

将上return 0;,才可以执行全部

如果想要打印查询到的每一列结果

再加上每一列相应的字段名

注意:对于外部传入回调函数的参数是无法修改的(具体原因可能是由于内部机制)

如:我们传入一个flag变量,然后出函数打印结果

我们在回调函数内对flag进行++

但出函数之后,值仍是0

如果查询不到结果,将会什么信息也不会输出

我们可以通过定义一个全局变量标志位进行判断,是否查询到数据

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

int flag=0;

void print_error(int ret, char *err, sqlite3 *db)
{
    if(ret != SQLITE_OK)
    {
        printf("%s:%s\n",err,sqlite3_errmsg(db));
        exit(-1);
    }
    printf("%s:successfully!\n",err);
}

int my_sqlite_callback(void *para,int columnCount,char **columnValue,char**columnName)
{
    printf("columnCount = %d\n", columnCount);
    flag=1;
    for(int i = 0; i < columnCount;i++)
    {
        printf("%s:%s|",columnName[i],columnValue[i]);
    }
    printf("\n");
    return 0;
}

int main(int argc, char const *argv[])
{
    if(argc != 2)
    {
        printf("Please input db name!\n");
        exit(-1);
    }

    sqlite3 *db;
    char *errmsg;
    char sql[1024] = {0};

    int ret = sqlite3_open(argv[1],&db);

    print_error(ret,"sqlite open",db);

    printf("sqlite open db successfully!\n");


    strcpy(sql,"select * from student where name = 'zhangsan'");
    sqlite3_exec(db,sql,my_sqlite_callback,NULL,&errmsg);
    print_error(ret,"select",db);
    if(flag==0)
    {
        printf("The data queried is empty!\n");
    }
    sqlite3_close(db);
    return 0;
}

全缓冲查询

API介绍

与sqlite_exec不同,sqlite3_get_table是专门用于查询数据的,通过一次性将所有查询到的数据缓冲起来

参数1:数据库文件句柄

参数2:数据库SQL语句

参数3:三维指针,用于保存查询到的结果

参数4:查询到的结果总共的行数

参数5:查询到的结果总共的列数

参数6:保存查询出错的信息

以三维指针为例,我们可以创建一个变相的二维数组:

可以想象创建一个长方体

char ***result;

result = (char***)malloc(sizeof(char**)*4);创建四个存储空间

*result = (char**)malloc(sizeof(char*)*4);每个存储空间里再创建四个存储空间

**result = (char*)malloc(sizeof(char)*4);每个存储空间里再创建一个字符串数组

最终在逻辑上形成16个连续的存储空间(物理上不连续)

我们可以通过三次for循环来创建

但访问方式仍是一维数组的访问方式,因为通过指针创建的空间,本质还是链式的,不是真正的多维数组

实际应用

实际使用时,我们需要定义一个二维指针,将它的地址作为参数传入,查询到的数据都将保存在二维指针中

发现打印是从字段开始打印的,最后少了一行数据,这是因为保存的数据包括了字段那一行,但是返回的行数nrow只算了实际数据的行数

因此需要改正如下:

这样输出的结果就是正确的了

最后一定要记得调用释放空间函数sqlite3_free_table,因为库函数sqlite3_get_table的内部分配了堆区空间

全缓冲查询程序如下:

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

void print_error(int ret, char *err, sqlite3 *db)
{
    if(ret != SQLITE_OK)
    {
        printf("%s:%s\n",err,sqlite3_errmsg(db));
        exit(-1);
    }
    printf("%s:successfully!\n",err);
}


int main(int argc, char const *argv[])
{
    if(argc != 2)
    {
        printf("Please input db name!\n");
        exit(-1);
    }

    sqlite3 *db;
    char *errmsg;
    char sql[1024] = {0};

    int ret = sqlite3_open(argv[1],&db);

    print_error(ret,"sqlite open",db);

    printf("sqlite open db successfully!\n");

    char **result;
    int nrow;
    int ncolumn;
    strcpy(sql,"select * from student");
    ret = sqlite3_get_table(db,sql,&result,&nrow,&ncolumn,&errmsg);
    print_error(ret,"select",db);

    for(int i = 1; i<=nrow; i++)
    {
        for(int j = 0; j < ncolumn; j++)
        {
            printf("%s|",result[i*ncolumn+j]);
        }
        printf("\n");
    }

    sqlite3_free_table(result);
    sqlite3_close(db);
    return 0;
}

字节缓冲查询

API介绍

sqlite3_prepare

作用:把SQL语句转成字节码,由后面的执行函数去执行,将查询到的数据做字节缓冲

参数1:数据库文件句柄

参数2:SQL语句

参数3:SQL语句的最大字节数,一般设为-1

参数4:Statement句柄,即字节序句柄

参数5:SQL语句无用部分的指针,一般设为NULL

字节缓冲查询还涉及到了以下函数:

  • sqlite3_step:从第一行开始查询,每次查询一行,每调用一次该函数会继续查询下一行数据,数据不为空则返回SQLITE_ROW

  • sqlite3_column_count:获取结果的列数

  • sqlite3_column_text:获取程序的结果当前行中每一列的数据

  • sqlite3_finalize:用于销毁字节序句柄

实际应用

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

void print_error(int ret, char *err, sqlite3 *db)
{
    if(ret != SQLITE_OK)
    {
        printf("%s:%s\n",err,sqlite3_errmsg(db));
        exit(-1);
    }
    printf("%s:successfully!\n",err);
}


int main(int argc, char const *argv[])
{
    if(argc != 2)
    {
        printf("Please input db name!\n");
        exit(-1);
    }

    sqlite3 *db;
    char *errmsg;
    char sql[1024] = {0};

    int ret = sqlite3_open(argv[1],&db);

    print_error(ret,"sqlite open",db);

    printf("sqlite open db successfully!\n");

    
    int rc,i,j;
    int ncolumn;
    sqlite3_stmt *stmt;
    strcpy(sql,"select * from student");

    rc = sqlite3_prepare(db,sql,-1,&stmt,NULL);
    if(rc)
    {
        printf("query fail!\n");
    }
    else
    {
        printf("query success!\n");
        rc = sqlite3_step(stmt);//查询成功,则返回值rc==SQLITE_ROW
        ncolumn = sqlite3_column_count(stmt);//获取列数
        while(rc == SQLITE_ROW)
        {
            for(i = 0;i<ncolumn;i++)
            {
                printf("%s|",sqlite3_column_text(stmt,i));//获取每一列的数据
            }
            printf("\n");
            rc = sqlite3_step(stmt);//继续获取下一行数据
        }
    }
    sqlite3_finalize(stmt);
    sqlite3_close(db);
    return 0;
}

三种查询方式总结

回调函数查询内存开销小,但查询效率相对较低;全缓冲查询的查询效率高,但是内存消耗大;字节缓冲查询兼具查询效率和低开销。(优先使用第三种查询方法)

sqlite实现C语言自定义函数封装

由于数据库提供的API接口过于复杂,使用的过程顺序也很繁琐,所以对于原生态的API在实际工作开发中,会进行一层封装,减少调用传参,减少调用次数,增加代码可读性,提高开发效率。

可封装如下:包括创建数据库、建表、插入数据、查询数据、删除数据

database.h

#ifndef _DATABASE_H_
#define _DATABASE_H_
 
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <libgen.h>
#include <sqlite3.h>
 
extern int open_database(sqlite3 **db,char *database_name);
extern int create_table(sqlite3 **db,char *table_name,char *table_attribute);
extern int insert_data(sqlite3 **db,char *table_name,char *attr,char *msg);
extern int query_data(sqlite3 **db,char ***azResult,char *table_name);
extern int delete_data(sqlite3 **db,char *table_name);
 
#endif

 database.c

#include "database.h"
 
char            sql[128];
char            *zErrMsg=NULL;
int             nrow=0;
int             ncolumn = 0;
 
 
int open_database(sqlite3 **db,char *database_name)
{
        int             len;
 
        len = sqlite3_open(database_name,db);
        if(len)
        {
                printf("Open database name %s failure.\n",database_name);
                sqlite3_close(*db);
                return -1;
        }
        printf("Open a sqlite3 database name %s successfully!\n",database_name);
 
        return 0;
}
 
int create_table(sqlite3 **db,char *table_name,char *table_attribute)
{
 
 
        snprintf(sql,sizeof(sql),"CREATE TABLE %s(%s);",table_name,table_attribute);
        //log_info("sql=%s\n",sql);
 
        //sql="CREATE TABLE test(TEST CHAR(100));";
 
        if(sqlite3_exec(*db,sql,NULL,NULL,&zErrMsg)!=SQLITE_OK)
        {
                printf("Table %s already exist\n",table_name);
        }
        else
        {
                printf("Create table %s successfully\n",table_name);
        }
 
}
 
int insert_data(sqlite3 **db,char *table_name,char *attr,char *msg)
{
        snprintf(sql,sizeof(sql),"INSERT INTO %s(%s) VALUES('%s');",table_name,attr,msg);   //插入数据
 
        if(sqlite3_exec(*db,sql,NULL,NULL,&zErrMsg)!=SQLITE_OK)
        {
                sqlite3_close(*db);
                printf("Insert %s to table %s failure:%s\n",msg,table_name,strerror(errno));
                return -1;
        }
        printf("Insert %s to table %s successfully\n",msg,table_name);
 
        return 0;
}
 
 
int query_data(sqlite3 **db,char ***azResult,char *table_name)
{
        snprintf(sql,sizeof(sql),"select *from %s;",table_name);
        //sql="select *from test";
 
        if(sqlite3_get_table(*db,sql,azResult,&nrow,&ncolumn,&zErrMsg)!=SQLITE_OK)
        {
                sqlite3_close(*db);
                printf("Select *from %s failure\n",table_name);
                return -1;
        }
        printf("There are %d pieces of data in table %s\n",nrow,table_name);
 
        return nrow;
}
 
int delete_data(sqlite3 **db,char *table_name)
{
        snprintf(sql,sizeof(sql),"delete from %s;",table_name);
        //sql="delete from test";
 
        if(sqlite3_exec(*db,sql,NULL,NULL,&zErrMsg)!=SQLITE_OK)
        {
                sqlite3_close(*db);
                printf("Delete from %s failure\n",table_name);
                return -1;
        }
        printf("Delete data from table %s successfully!\n",table_name);
 
        return 0;
}

test_database.c

#include "database.h"
 
 
int main(void)
{
        sqlite3 *db;
        char    **azResult=NULL;
 
        if( open_database(&db,"test.db")<0 )
                return -1;
 
        if( create_table(&db,"test","TEST CHAR(100)")<0 )
                return -2;
 
        if( insert_data(&db,"test","TEST","Test nihao")<0 )
                return -3;
 
        if( query_data(&db,&azResult,"test")<0 )
                return -4;
 
        if( delete_data(&db,"test")<0 )
                return -5;
 
        if( query_data(&db,&azResult,"test")<0 )
                return -6;
 
        return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/942326.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux】【驱动】自动创建设备节点

【Linux】【驱动】自动创建设备节点 续驱动代码操作指令linux端从机端 续 这里展示了如何自动的方式去创建一个字符类的节点 下面就是需要调用到的程序 函数 void cdev_init(struct cdev *, const struct file_operations *);第一个参数 要初始化的 cdev 第二个参数 文件操作…

【微服务部署】01-Kubernetes部署流程

文章目录 部署1. Kubernetes是什么2. Kubernetes的优势3. 环境搭建4. 应用部署 部署 1. Kubernetes是什么 Kubernetes是一个用于自动部署、扩展和管理容器化应用程序的开源系统 2. Kubernetes的优势 自动化容器部署资源管理与容器调度服务注册发现与负载均衡内置配置与秘钥…

STM32 CUBEMX CAN通信数据发送失败原因分析

CAN通信是一种数据通信协议&#xff0c;用于在不同设备之间进行通信。它是一种高效的、实时的、可靠的、多主机的、串行通信系统&#xff0c;通常用于汽车电子、工业自动化等领域。CAN通信协议是由德国BOSCH公司于1986年引入&#xff0c;并在欧洲和日本广泛使用。CAN通信具有独…

uniapp热更新

首先热更新需要wgt包&#xff1b; 其次先了解这两个组件 下载的方法 安装的组件 场景&#xff1a; 当你项目的js文件或者页面文件或者静态图片文件css文件更新的时候可以走热更新&#xff1b; 而当你安装新的组件插件或者开启新的权限等功能的时候就无法通过热更新进行更新了…

自然语言处理(三):基于跳元模型的word2vec实现

跳元模型 回顾一下第一节讲过的跳元模型 跳元模型&#xff08;Skip-gram Model&#xff09;是一种用于学习词向量的模型&#xff0c;属于Word2Vec算法中的一种。它的目标是通过给定一个中心词语来预测其周围的上下文词语。 这节我们以跳元模型为例&#xff0c;讲解word2vec的…

C语言(第三十四天)

1. 二进制 其实我们经常能听到2进制、8进制、10进制、16进制这样的讲法&#xff0c;那是什么意思呢&#xff1f;其实2进制、8进制、10进制、16进制是数值的不同表示形式而已。 比如&#xff1a;数值15的各种进制的表示形式: 15的2进制&#xff1a;1111 15的8进制&#xff1a;1…

开源项目的文档:为什么它如此重要?

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

2023.8.28日论文阅读

文章目录 NestFuse: An Infrared and Visible Image Fusion Architecture based on Nest Connection and Spatial/Channel Attention Models(2020的论文)本文方法 LRRNet: A Novel Representation Learning Guided Fusion Network for Infrared and Visible Images本文方法学习…

JVM 内存大对象监控和优化实践

作者&#xff1a;vivo 互联网服务器团队 - Liu Zhen、Ye Wenhao 服务器内存问题是影响应用程序性能和稳定性的重要因素之一&#xff0c;需要及时排查和优化。本文介绍了某核心服务内存问题排查与解决过程。首先在JVM与大对象优化上进行了有效的实践&#xff0c;其次在故障转移与…

jmeter传参base64卡顿如何解决

部分接口需要传图片base64格式参数&#xff0c;但是输入转为base64格式的图片参数&#xff0c;jmeter直接卡死&#xff0c;甚至电脑也卡死&#xff0c;此时&#xff0c;只需要去掉文件头描述&#xff1a;data:image/jpeg;base64, 即可

Element Plus 日期选择器的使用和属性

element plus 日期选择器如果如果没有进行处理 他会返回原有的属性值data格式 如果想要获取选中的日期时间就需要通过以下的代码来实现选中的值 format"YYYY/MM/DD" value-format"YYYY-MM-DD" <el-date-pickerv-model"formInline.date" type&…

《Flink学习笔记》——第五章 DataStream API

一个Flink程序&#xff0c;其实就是对DataStream的各种转换&#xff0c;代码基本可以由以下几部分构成&#xff1a; 获取执行环境读取数据源定义对DataStream的转换操作输出触发程序执行 获取执行环境和触发程序执行都属于对执行环境的操作&#xff0c;那么其构成可以用下图表示…

AD画PCB时设置的中文丝印乱码

AD画PCB时设置的中文丝印乱码怎么解决&#xff1f; 画好PCB后通常会加一些没有电气属性的丝印或者板号&#xff0c;有时用英文有时用中文&#xff0c;通常用英文或者数字都能直接显示&#xff0c;但是用中文时显示的就是乱码&#xff1b;因为字符串放置好后默认的字体是“比划…

使用el-tag和el-select组件实现标签的增删

第一步 点击按钮&#xff0c;弹出博客所拥有的标签列表的气泡 效果图 第二步 选择标签列表中的标签进行添加 效果图 第三步 实现标签的移除 效果图 页面编写 <!-- 标签模块 start--><el-popover trigger"click" placement"top" :width&quo…

设计模式—策略模式

目录 一、定义 二、特点 三、优点 四、缺点 五、实例 六.涉及到的知识点 1、一个类里面有哪些东西&#xff1f; 2、类和实例 什么是类&#xff1f; 什么是实例&#xff1f; 什么是实例化&#xff1f; 3、字段和属性 什么是字段&#xff1f; 属性是什么&#xff1…

自实现getprocaddress(名称查找或者序号查找)

通过名称去找 // MyGETPRCOADDRESS.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 //#include <iostream> #include<Windows.h>/*WINBASEAPI //导出不需要使用&#xff0c;那么我们注释掉*/ FARPROC WINAPI MyGetProcAddress(_In_ HMO…

SSM学习内容总结(Spring+SpringMVC+MyBatis)

目录 1、什么是SSM2、学习内容汇总2.1、Spring2.2、SpringMVC2.3、MyBatis2.4、SSM整合 &#x1f343;作者介绍&#xff1a;准大三本科网络工程专业在读&#xff0c;持续学习Java&#xff0c;努力输出优质文章 &#x1f341;作者主页&#xff1a;逐梦苍穹 &#x1f440;近期目标…

SpringBoot初级开发--加入Log4j进行日志管理打印(6)

日志记录在整个java工程开发中占着很重要的比重&#xff0c;因为很多问题的排查需要通过日志分析才能确认。在SpringBoot中我用得最多的就是log4j这个日志框架。接下来我们具体配置log4j. log4j定义了8个级别的log&#xff08;除去OFF和ALL&#xff0c;可以说分为6个级别&#…

Jackpack - Hilt

一、概念 类中使用的某个对象不是在这个类中实例化的&#xff08;如Activity无法手动实例化使用&#xff09;&#xff0c;而是通过外部注入&#xff08;从外部传入对象后使用&#xff09;&#xff0c;这种实现方式就称为依赖注入 Dependency Injection&#xff08;简称DI&#…

软考A计划-网络工程师-常用计算公式汇总

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…