在线词典项目(附源码以及解析)(完整工程下载)

在线词典项目(附源码以及解析)

项目设计总览

在这里插入图片描述

项目要求:

1、用户的 注册、登录、注销 功能设计:

注册:将用户名和密码保存至数据库当中(要判断用户名是否被注册)

登录:用户输入的数据将会和数据库中的数据进行比较,如果一致登录成功否则失败

2.登录成功**(词典功能设计)**

词典的翻译、历史记录、退出 功能设计

翻译:用户输入单词,服务器从本地文件中查询到相应单词后将翻译传送至客户端

历史记录查询:用户每次查询单词,将用户查询的单词以及对应的翻译,查询的时间存储到数据库当中用户选择该功能的时候,就可以查询之前查看过得单词

项目成果展示:

用户注册

(客户端)

(服务器)

在这里插入图片描述

用户登录

(客户端)

用户登录成功进入进入二级菜单
在这里插入图片描述

查询单词

(客户端)
在这里插入图片描述

(服务器)
在这里插入图片描述

历史查词记录

(客户端)
在这里插入图片描述

(服务器)
在这里插入图片描述

源代码

client.h
#ifndef _CLIENT_H
#define _CLIENT_H

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

// 结构体保存命令,传输到服务器的一些基本信息
struct msg
{
    char cmd;           // 命令识别号
    char hostname[20];  // 用户名
    char passwords[20]; // 密码
    char word[20];      // 查询的单词
    char data[256];     // 存放翻译单词的信息
    char e_msg[100];    // 处理信息
};

struct msg mymsg; // 我们将所要从客户端发送的数据,封装到该结构体中
int ret;          // ret用作接收函数的返回值(return)


int strcp(char *str1, char *str2);
int register_func(struct msg aabb, int sockfd); // 注册函数
int login_func(struct msg aabb, int sockfd);    // 登录函数
int query_word(struct msg aabb, int sockfd);    // 查词请求
int query_records(struct msg aabb, int sockfd); // 历史查词
void init_struct(struct msg aabb);				// 初始化结构体
void print_struct(struct msg aabb);				// 用于调试查看结构体成员

#endif
client.c
#include "client.h"   //引用自己定义的头文件使用“”括上


// 字符串的拷贝  将str1中内容拷贝到str2 中
int strcp(char *str1, char *str2)
{
	int i, j;
	j = strlen(str1) - 1;
	for (i = 0; i < j; i++)
	{
		str2[i] = str1[i];
	}
	return 0;
}

// 注册函数功能
int register_func(struct msg aabb, int sockfd)
{
	char name[20] = "\0";
	char temp1[20] = "\0";
	char temp2[20] = "\0";
	int flag = 1;
	aabb.cmd = 1;

	printf("请输入注册的用户名:(用户名将作为用户的登录账户!) \n");
	getchar();
	fgets(name, sizeof(name), stdin);
	strcp(name, aabb.hostname);
	// printf("%s\n",name);   //之所以前面要使用getchar,自己解掉注释输出看一下用户名

mima:
	printf("请输入登录密码:\n");
	fgets(temp1, sizeof(temp1), stdin);

	printf("请再次输入登录密码:\n");
	fgets(temp2, sizeof(temp2), stdin);

	if (!strncmp(temp1, temp2, strlen(temp2)))
	{
		strcp(temp2, aabb.passwords);
	}
	else
	{
		printf("请确保两次密码相同!\n");
		system("clear");
		goto mima;
	}

	//----上面已经配置好需要发送的结构体了,下面开始将结构体传输到服务器:

	send(sockfd, &aabb, sizeof(aabb), 0);

	return 0;
}

int login_func(struct msg aabb, int sockfd)
{
	aabb.cmd = 2;
	//==============用户验证=====================
	printf("请输入用户名: \n");
	getchar();
	scanf("%s", aabb.hostname);
	// printf("用户名:%s\n", aabb.hostname);

	printf("请输入用户密码:  \n");
	scanf("%s", aabb.passwords);
	// printf("密码:%s\n", aabb.passwords);

	ret = send(sockfd, &aabb, sizeof(aabb), 0);
	if (ret < 0)
	{
		perror("send");
		return -1;
	}

	ret = recv(sockfd, &aabb, sizeof(aabb), 0);
	if (ret == -1)
	{
		perror("recv");
		return -1;
	}
	printf("aabb.e_msg=%s\n", aabb.e_msg);

	if (!strncmp(aabb.e_msg, "OK", 2))
	{
		printf("登录成功!\n");
	}
	else
	{
		printf("登录失败!\n");
		return -1;
	}

	//====================================================

	return 0;
}

// 用户登录成功后的查询功能
int query_word(struct msg aabb, int sockfd)
{
	//===============向服务器发起查词请求==================================
	aabb.cmd = 4;

	printf("请输入需要查询的单词: \n");
	// getchar();
	scanf("%s", aabb.word);

	ret = send(sockfd, &aabb, sizeof(aabb), 0);
	if (ret == -1)
	{
		perror("send");
		return -1;
	}
	else
	{
		printf("正在查询中...... \n\n");
	}

	//===================接受服务器的查词结果======================

	ret = recv(sockfd, &aabb, sizeof(aabb), 0);
	if (ret == -1)
	{
		perror("recv");
		return -1;
	}
	// print_struct(aabb);
	// printf("data=%s\n",aabb.data);
	// printf("e_msg=%s\n",aabb.e_msg);
	if (!strncmp(aabb.e_msg, "ok", 2))
	{
		printf("查询结果:%s\n", aabb.data);
	}
	else
	{
		printf("单词表中无此单词,请检查单词拼写是否有误\n");
	}

	return 0;
}

int query_records(struct msg aabb, int sockfd)
{
	aabb.cmd = 5;
	ret = send(sockfd, &aabb, sizeof(aabb), 0);
	printf("record =%s\n", aabb.hostname);
	if (ret == -1)
	{
		perror("send");
		return -1;
	}

	// 接收服务器的,传递回来的历史记录信息
	while (1)
	{
		recv(sockfd, &aabb, sizeof(aabb), 0);
		if (!strncmp(aabb.e_msg, "end", 3))
		{
			break;
		}
		// 输出历史信息
		printf("%s\n", aabb.data);
	}

	return 0;
}

void init_struct(struct msg aabb)
{
	// memset(&aabb, 0, sizeof(aabb));
	memset(&aabb.cmd, 0, sizeof(aabb.cmd));
	memset(&aabb.passwords, 0, sizeof(aabb.passwords));
	memset(&aabb.word, 0, sizeof(aabb.word));
	memset(&aabb.data, 0, sizeof(aabb.data));
	memset(&aabb.e_msg, 0, sizeof(aabb.e_msg));
}

// 该函数是楼主为了测试查看数据方便,所编写
void print_struct(struct msg aabb)
{
	printf("cmd=%c \n", aabb.cmd);
	printf("hostname=%s \n", aabb.hostname);
	printf("passwords=%s \n", aabb.passwords);
	printf("word=%s \n", aabb.word);
	printf("data=%s \n", aabb.data);
	printf("e_msg=%s \n", aabb.e_msg);
}
main_client.c
/*************************************************************************
	> File Name: main_client.c
	> Author: xiaoxiongbenbi
	> Mail: aabb@qq.com
 ************************************************************************/

#include "client.h"  	  //引用自己定义的头文件使用“”括上


int sockfd;       // sockfd为套接字文件描述符
int cmd, n;       // cmd 用作一级菜单的判定标志   n用作二级菜单的判定标志
struct sockaddr_in servaddr;
socklen_t addrlen = sizeof(struct sockaddr_in);
char buf[1024] = "\0";


int main(int argc, const char *argv[])
{
    if(argc != 3)   //argc表示main函数中的参数个数,不了解可以自己搜索main函数的参数
	{
		printf("Usage:%s ip port.\n",argv[0]);//没有传IP和端口号
	}

	// 1、创建套接字
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd == -1)
	{
		perror("socket");
		return -1;
	}

	// 2、绑定网络信息
	memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(atoi(argv[2]));
	servaddr.sin_addr.s_addr = inet_addr(argv[1]);

	// 3、发送连接请求
	ret = connect(sockfd, (struct sockaddr *)&servaddr, addrlen);
	if (ret == -1)
	{
		perror("connect");
		return -1;
	}

	recv(sockfd, buf, sizeof(buf), 0);
	if (!strncmp(buf, "ok", 2))
	{
		printf("已经成功连接到服务器\n");
	}

	// 4、功能实现
	while (1)
	{
		// 打印用户登录提示
		printf("||*******输入数字1,2,3 选择下列选项********||\n");
		printf("\n");
		printf("||***********小笨熊在线词典***************||\n");
		printf("||  1-注册  ||  2-登录    ||    3-退出    ||\n");
		printf("||***************************************||\n");
		scanf("%d", &cmd);
		switch (cmd)
		{
		case 1:
			init_struct(mymsg);
			register_func(mymsg,sockfd);
			system("clear");
		case 2:
			init_struct(mymsg);
			while (1)
			{
				if (0 == login_func(mymsg,sockfd))
				{
					// 登录成功进入二级菜单
					goto next;
					break;
				}
				system("clear");
			}
		case 3:
			close(sockfd);
			exit(0);
			break;
		default:
			system("clear");
			printf("请输入数字1,2,3\n");
			break;
		}
	}

next:
	while (1)
	{
        printf("**************输入数字1,2,3 选择下列选项**************\n");
		printf("****************************************************\n");
		printf("*  1--查询单词    2--历史查词记录       3--退出      *\n");
		printf("****************************************************\n");
		printf("Please choose:  ");
		scanf("%d", &n);
		getchar();

		switch (n)
		{
		case 1:
			init_struct(mymsg);
			query_word(mymsg,sockfd);
			break;
		case 2:
			init_struct(mymsg);
			query_records(mymsg,sockfd);
			break;
		case 3:
			close(sockfd);
			exit(0);
			break;
		default:
			printf("Invalid data cmd.\n");
		}
	}

	// 5、关闭文件描述符号
	close(sockfd);

	return 0;
}
server.h
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sqlite3.h>
#include <time.h>

// 结构体保存命令,传输到客户端的一些基本信息
struct msg
{
    char cmd;           // 命令识别号
    char hostname[20];  // 用户名
    char passwords[20]; // 密码
    char word[20];      // 查询的单词
    char data[256];     // 存放翻译单词的信息
    char e_msg[100];    // 处理信息
};

int ret;
struct msg sermsg;

int strcp(char *str1, char *str2);                                                            // 将str1 复制到str2中
int create_table(sqlite3 *db);                                                                // 创建存放用户的users表   和  存放查词记录的record表
int insert_Data_to_users(sqlite3 *db, char *buf, char *UID, char *hostname, char *passwords); // 向users表中插入数据
int insert_Data_to_record(sqlite3 *db, char *buf, char *hostname, char *data);                // 向record表中插入数据

int register_func(struct msg aabb, sqlite3 *db, char *buf);               // 注册函数
int login_func(sqlite3 *db, int connfd, struct msg aabb);                 // 登录函数
static int callback(void *data, int argc, char **argv, char **azColName); // 回调函数
int query_word(sqlite3 *db, char *buf, int connfd, struct msg aabb);
int query_records(sqlite3 *db, int connfd, struct msg aabb);

static inline void get_time(char *buf); // 获取系统时间                                

void init_struct(struct msg aabb);  // 初始化结构体
void print_struct(struct msg aabb); // 该函数用于方便调试结构体成员
server.c
/*************************************************************************
	> File Name: server.c
	> Author: xiaoxiongbenbi
	> Mail: aabb@qq.com
 ************************************************************************/

#include "server.h"      //引用自己定义的头文件使用“”括上

// 字符串的拷贝  将str1中内容拷贝到str2 中
int strcp(char *str1, char *str2)
{
	int i, j;
	j = strlen(str1);
	for (i = 0; i < j; i++)
	{
		str2[i] = str1[i];
	}

	return 0;
}

// 创建存放用户的users表   和  存放查词记录的record表
int create_table(sqlite3 *db)
{
	// 创建表准备
	// UID 字段  自动增长
	const char *creat1 = "create table if not exists users(Data char,UID  INTEGER PRIMARY KEY autoincrement,hostname char unique ,passwords char	);\n";
	const char *creat2 = "create table if not exists record(time char,hostname char,data char);\n";

	// printf("%s", creat1);
	// printf("%s", creat2);

	char *p2 = 0;
	ret = sqlite3_exec(db, creat1, 0, 0, &p2); // 我的理解是:sqlite3_exec()作为API,收到我的命令creat表,告诉数据库执行相关命令。
	if (ret == 0)
	{
		printf("用户注册表users创建成功!\n");
	}
	else
	{
		printf("%s", p2);
	}

	// 创建历史查询记录表
	ret = sqlite3_exec(db, creat2, 0, 0, &p2);
	if (ret == 0)
	{
		printf("查词记录表record创建成功!\n");
	}
	else
	{
		printf("%s", p2);
	}

	sqlite3_free(p2);
	return 0;
}

// 向users表中插入数据
int insert_Data_to_users(sqlite3 *db, char *buf, char *UID, char *hostname, char *passwords)
{
	char *rt;
	char *p2 = 0;
	// 这里的UID我们设置的是主键自增长,不需要 m对其进行设置
	rt = sqlite3_mprintf("INSERT INTO users VALUES('%s',NULL,'%s','%s')", buf, hostname, passwords); // 插入数据准备

	ret = sqlite3_exec(db, rt, 0, 0, &p2); // 执行命令
	return ret;
}

// 向record表中插入数据
int insert_Data_to_record(sqlite3 *db, char *buf, char *hostname, char *data)
{
	char *rt;
	char *p2 = 0;
	// record(time char,hostname char,data char)
	rt = sqlite3_mprintf("INSERT INTO record VALUES('%s','%s','%s')", buf, hostname, data); // 插入数据准备

	ret = sqlite3_exec(db, rt, 0, 0, &p2); // 执行命令
	return ret;
}

// 词典用户注册函数功能
int register_func(struct msg aabb, sqlite3 *db, char *buf)
{
	memset(buf, 0, sizeof(buf)); // 清空存放系统时间 数组buff
	get_time(buf);				 // 获取时间放在buff中

	// 向注册表中插入数据
	// int insert_Data_to_users( char *UID, char *hostname, char *passwords);
	ret = insert_Data_to_users(db, buf, NULL, aabb.hostname, aabb.passwords);
	if (ret == -1)
	{
		printf("数据插入失败!\n");
		return -1;
	}
	else
	{
		printf("数据插入成功!\n");
	}

	// sqlite3_close(db);
	return 0;
}

// 登录函数功能
int login_func(sqlite3 *db, int connfd, struct msg aabb)
{
	char sql[512] = "\0"; // 128太小会警告
	char *errmsg;
	int nrow;
	int ncloumn;
	char **resultp;

	sprintf(sql, "select * from users where hostname = '%s' and passwords = '%s' ;", aabb.hostname, aabb.passwords);
	printf("%s\n", sql);

	if (sqlite3_get_table(db, sql, &resultp, &nrow, &ncloumn, &errmsg) != SQLITE_OK)
	{
		printf("%s\n", errmsg);
		return -1;
	}
	else
	{
		printf("get_table ok!\n");
	}

	// 查询成功,数据库中拥有此用户
	if (nrow == 1)
	{
		strcpy(aabb.e_msg, "OK"); // 这里把OK放进data里,client.c中才会那么比较
		send(connfd, &aabb, sizeof(aabb), 0);
		return 0;
	}

	if (nrow == 0) // 密码或者用户名错误,或者写else就可以
	{
		strcpy(aabb.e_msg, "usr/passwd wrong.");
		send(connfd, &aabb, sizeof(aabb), 0);
		return -1;
	}
}

// 单词查询
int query_word(sqlite3 *db, char *buf, int connfd, struct msg aabb)
{
	FILE *fd;
	char temp[256] = "\0";
	char *p;
	int len, result; // len 记录单词的长度
	// 记录查询的单词长度
	len = strlen(aabb.word);
	printf("len = %d\n", len);
	// 打开保存在服务器本地的dict.txt(单词表)
	if (NULL == (fd = fopen("dict.txt", "r")))
	{
		strcpy(aabb.e_msg, "open dictionary file error!\n");
		if (-1 == send(connfd, &aabb, sizeof(aabb), 0))
		{
			perror("send");
			return -1;
		}
	}
	printf("本地单词表文件已经打开!\n");
	// 在dict.txt 中查找所要搜寻的单词
	/***
	 * 因为在dict.txt我们的单词是每一行存储一个,
	 * 所以可以利用fgets函数的特点 : 读到换行符号就会停止
	 */

	printf("word=%s\n", aabb.word);
	while (fgets(temp, 256, fd) != NULL)
	{
		// printf("temp = %s\n", temp);

		//  比较单词 找到之后直接跳出对比   没有找继续直到整个文件全部找完
		result = strncmp(aabb.word, temp, len);
		if (result == 0)
		{
			break;
		}

		memset(buf, 0, sizeof(buf));
	}

	// 	找到或者找完之后   进行判断看
	if (result == 0)
	{
		strcp(temp, aabb.data);
		strcpy(aabb.e_msg, "ok");
		// 下列注释语句用作调试
		//  print_struct(aabb);
		//  printf("data=%s\n",aabb.data);
		memset(buf, 0, sizeof(buf));
		get_time(buf); // 获取时间放在buff中

		// 向查词历史表中插入数据
		// record(time char,hostname char,data char)
		ret = insert_Data_to_record(db, buf, aabb.hostname, aabb.data);
		if (ret == -1)
		{
			printf("数据插入record表失败!\n");
			return -1;
		}
		else
		{
			printf("数据插入record表成功!\n");
		}
	}
	else
	{
		strcpy(aabb.e_msg, "error");
	}

	ret = send(connfd, &aabb, sizeof(aabb), 0);
	if (ret == -1)
	{
		perror("send");
		return -1;
	}

	fclose(fd);
	return 0;
}

// 得到查询结果,并且需要将历史记录发送给客户端
int history_callback(void *params, int column_size, char **column_value, char **column_name)
{
	int connfd;  //在回调函数中要想通过套接字发送数据,就需要设置一个形参来接收sqlite3_exec传递过来的参数(第四个参数,也就是套接字文件描述符)
	connfd = *((int *)params);
	sprintf(sermsg.data, "%s , %s , %s ", column_value[0], column_value[1], column_value[2]);
	printf("%s\n", sermsg.data);
	send(connfd, &sermsg, sizeof(sermsg), 0);
	return 0;
}

// 历史查词记录
int query_records(sqlite3 *db, int connfd, struct msg aabb)
{
	char sql[512] = "\0";
	char *errmsg;

	sprintf(sql, "select * from record where hostname = '%s'", aabb.hostname);

	// 查询数据库
	if (sqlite3_exec(db, sql, history_callback, (void *)&connfd, &errmsg) != SQLITE_OK)
	{
		printf("%s\n", errmsg);
	}
	else
	{
		printf("Query record done.\n");
	}

	strcp("end", aabb.e_msg);
	printf("aabb=%s\n", aabb.e_msg);
	send(connfd, &aabb, sizeof(aabb), 0);

	return 0;
}

static inline void get_time(char *buf)
{
	struct tm *tm_ptr;
	time_t the_time;
	(void)time(&the_time);
	tm_ptr = gmtime(&the_time);
	sprintf(buf, "%02d-%02d-%02d  %02d:%02d:%02d", tm_ptr->tm_year + 1900, tm_ptr->tm_mon + 1, tm_ptr->tm_mday, (tm_ptr->tm_hour) + 8, tm_ptr->tm_min, tm_ptr->tm_sec);
}

// 初始化结构体
void init_struct(struct msg aabb)
{
	memset(&aabb, 0, sizeof(aabb));
}

// 该函数用于方便调试结构体成员
void print_struct(struct msg aabb)
{
	printf("cmd=%c-- \n", aabb.cmd);
	printf("hostname=%s-- \n", aabb.hostname);
	printf("passwords=%s-- \n", aabb.passwords);
	printf("word=%s-- \n", aabb.word);
	printf("data=%s-- \n", aabb.data);
	printf("e_msg=%s-- \n", aabb.e_msg);
}
main_server.c
#include "server.h"		  //引用自己定义的头文件使用“”括上

int sockfd, connfd;
sqlite3 *db = NULL;
struct sockaddr_in servaddr, cliaddr;
socklen_t addrlen = sizeof(struct sockaddr_in);

char buf[1024] = "\0";

int main(int argc, const char *argv[])
{
	if(argc != 3)   //argc表示main函数中的参数个数,不了解可以自己搜索main函数的参数
	{
		printf("Usage:%s ip port.\n",argv[0]);//没有传IP和端口号
	}

	// 1、打开数据库
	if (sqlite3_open("dir.db", &db) != SQLITE_OK)
	{
		printf("%s\n", sqlite3_errmsg(db));
		return -1;
	}
	else
	{
		printf("open dir.db success\n");
	}

	// 创建用户注册表和历史查词记录表
	create_table(db);

	// 1、创建套接字
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd == -1)
	{
		perror("socket");
		return -1;
	}

	// 2、绑定网络信息
	memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(atoi(argv[2]));
	servaddr.sin_addr.s_addr = inet_addr(argv[1]);

	ret = bind(sockfd, (struct sockaddr *)&servaddr, addrlen);
	if (ret == -1)
	{
		perror("bind");
		return -1;
	}

	// 3、监听套接字

	ret = listen(sockfd, 5);
	if (ret == -1)
	{
		perror("listen");
		return -1;
	}

	// 4、接收连接请求
	connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &addrlen);
	if (connfd == -1)
	{
		perror("accept");
		return -1;
	}
	send(connfd, "ok", 2, 0);

	printf("连接成功\n");

	// 5、功能实现
	while (1)
	{
		memset(buf, 0, sizeof(buf)); // 初始化存放时间的数组 buf
		ret = recv(connfd, &sermsg, sizeof(sermsg), 0);
		if (ret == -1)
		{
			perror("recv");
			return -1;
		}
		else
		{
			printf("%c\n", sermsg.cmd);
		}

		switch (sermsg.cmd)
		{
		case 1:
			// printf("%s\n", sermsg.hostname);
			// printf("%s\n", sermsg.passwords);
			register_func(sermsg, db, buf);
			break;
		case 2:
			login_func(db, connfd, sermsg);
			break;
		case 3:
			sqlite3_close(db);
			close(connfd);
			close(sockfd);
			break;
		case 4:
			query_word(db, buf, connfd, sermsg);
			break;
		case 5:
			query_records(db, connfd, sermsg);
			break;
		}

		init_struct(sermsg);
	}

	return 0;
}

gcc如何编译多个文件呢?

在项目中我们大部分的时间一般都在修改和调试程序,所以能使用脚本来编写执行程序的编译。就能够大大地提升开发中的效率。

下面我给出我在此项目中所编写的shell脚本。可以参考学习一下!

make.sh

第一种写法:

#########################################################################
# File Name: make.sh
# Author: xiaoxiongbenbi
# mail: aabb@qq.com
#########################################################################
#!/bin/bash

gcc -I. -c  main_server.c
gcc -I. -c  server.c
gcc -I. -c  main_client.c
gcc -I. -c  client.c
gcc client.o main_client.o -o client  
gcc server.o main_server.o -o server -lsqlite3  #server.c 中用到了sqlite3中的函数就必须链接sqlite3提供的函数库

rm *.o			#把生成的中间.o文件删除

echo "compliing success!"

第二种写法:

#########################################################################
# File Name: makefile.sh
# Author: xiaoxiongbenbi
# mail: aabb@qq.com
# Created Time: 2023年01月17日 星期二 16时28分38秒
#########################################################################
#!/bin/bash
gcc -I. main_server.c server.c -o server -lsqlite3
gcc -I. main_client.c client.c -o client 

echo "compliing success!"

下方连接文章中有更加详细的介绍(可供参考):

(6条消息) gcc运行多源文件c语言,使用gcc同时编译多个文件_weixin_39823200的博客-CSDN博客

sqlite3_exec()函数中的回调函数

关于sqlite3数据库的函数 ----sqlite3_exec()函数中的回调函数

不了解的同学可以翻看我前面的文章。

工程分享

链接:https://pan.baidu.com/s/1vl1yf4JRZtDYi-kuHiePOg?pwd=gsja
提取码:gsja
–来自百度网盘超级会员V4的分享

最后也欢迎大家的指正和批评!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值