从数据库中查找单词
我们知道,从文件中查找是一行一行的查找匹配,但是数据库就可以快速查找,节约时间;
我们先来讲一下大概思路(所有都为C语言);
首先使用access函数判断数据库字典有没有被创建,如果创建了就跳过创建这个步骤,要不然每次加载都会耗费很多时间(几乎1-2分钟)(等待的过程蛮漫长的);
这是使用到的头文件;
#include <sqlite3.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
/*********************定义的全局变量函数***************/
sqlite3 *pdb;
char temp[4096] = {0};
char *errmsg = NULL;
这是主函数的逻辑;
int main(void)
{
int ret = 0;
int ok = 0;
ok = access("/home/linux/Desktop/2024/20240822/dict.db",F_OK); //这个路径是我自己的,可以替换
if(0 == ok) //access函数的特性,F_OK是用于查看该文件是否存在,成功返回0
{
ret = Open(); //打开数据库
if(ret == -1) //错误处理
{
fprintf(stderr,"fail to sqlite_open:%s\n",sqlite3_errmsg(pdb));
return -1;
}
ret = word_put(); //传入想要查询的单词
if(ret == -1) //错误处理
{
fprintf(stderr,"fail to sqlite_exec:%s\n",errmsg);
sqlite3_free(errmsg); //释放掉错误码的空间
sqlite3_close(pdb); //关闭数据库
return -1;
}
return 0;
}
if(-1 == ok)
{
printf("不存在,开始创建\n");
ret = Open();
if(ret == -1)
{
fprintf(stderr,"fail to sqlite_open:%s\n",sqlite3_errmsg(pdb));
return -1;
}
ret = word_creat();
if(ret == -1)
{
return -1;
}
ret = word_put();
if(ret == -1)
{
fprintf(stderr,"fail to sqlite_exec:%s\n",errmsg);
sqlite3_free(errmsg);
sqlite3_close(pdb);
return -1;
}
}
sqlite3_close(pdb);
return 0;
}
打开数据库的函数;
/****************************************************/
int Open(void)
{
int ret = 0;
ret = sqlite3_open("dict.db",&pdb);
if(ret != SQLITE_OK)
{
return -1;
}
return 0;
}
/*****************************************************/
创建dict 数据库,并将字典加载到数据库中;
/*************************************************/
int word_creat()
{
int ret = 0;
FILE *fp = NULL;;
char *nsize = NULL;
char *pword = NULL;
char *pmean = NULL;
char tempword[1024] = {0};
sprintf(temp,"create table dict(id integer primary key asc, word text, mean text)");
ret = sqlite3_exec(pdb,temp,NULL,NULL,&errmsg); //使用sqlite3_exec函数执行语句
if(ret != SQLITE_OK) //错误处理
{
fprintf(stderr,"fail to sqlite_exec:%s\n",errmsg);
sqlite3_free(errmsg);
sqlite3_close(pdb);
return -1;
}
fp = fopen("dict.txt","r"); //打开字典文件
if(NULL == fp)
{
perror("fail to fopen dict.txt");
sqlite3_close(pdb);
return -1;
}
while(1)
{
memset(temp,0,sizeof(temp)); //对temp清零
nsize = fgets(temp,sizeof(temp),fp); //读取一行字符串
temp[strlen(temp)-1] = '\0';
if(nsize == NULL)
{
break;
}
pword = strtok(temp," "); //进行分割
pmean = strtok(NULL,"\r\n");
sprintf(tempword,"insert into dict values (NULL,\"%s\",\"%s\")",pword,pmean);
ret = sqlite3_exec(pdb,tempword,NULL,NULL,&errmsg); //插入单词和含义
if(ret != SQLITE_OK)
{
fprintf(stderr,"fail to sqlite_exec11:%s\n",errmsg);
sqlite3_free(errmsg);
sqlite3_close(pdb);
return -1;
}
}
fclose(fp);
return 0;
}
/*****************************************************/
查询单词;
/*****************************************************/
int word_put(void)
{
int ret = 0;
char input[1024] = {0};
printf("录入完成!\n");
memset(temp,0,sizeof(temp));
fgets(temp,sizeof(temp),stdin);
temp[strlen(temp)-1] = '\0';
sprintf(input,"select word ,mean from dict where word = \"%s\"",temp);
ret = sqlite3_exec(pdb,input,callback,NULL,&errmsg);
if(ret != SQLITE_OK)
{
return -1;
}
return 0;
}
/*************************************************/
回调函数;
/**************************************************/
int callback(void * arg, int column, char **pcontet, char **ptitle)
{
printf("%s : %s\n",pcontet[0],pcontet[1]); //打印查到的单词和意思
return 0;
}
/****************************************************/
这里面有几个有意思的点:
1.字符串的分割;
因为原字典里的单词长这样:a indef art one \r\n,
用strtok分割,第一次分割空格之前的单词,第二次分割到\r\n;
2.在加载过程中显示加载进度;
由于加载过程中太无聊,想要一点动态显示加载进度可以这样做,
//加载单词文件时,fseek将光标偏移到最末尾
fseek(fp, 0, SEEK_END);
//ftell获得光表的偏移量
tlen = ftell(fp);
//再将光标定义回开头
rewind(fp);
这样就获得了整个文件的大小;
//读取一行信息
pret = fgets(tmpbuff, sizeof(tmpbuff), fp);
//获得这一行的大小
clen = ftell(fp);
//转换成double型打印
printf("已加载: %.2lf\r", (double)clen / (double)tlen * 100);
//刷新
fflush(fp);
加上循环,这样就可以动态显示加载进度了;
3.显示加载用时;
想要显示整个文件加载到数据库需要多长时间可以这样做;
在加载文件之前使用gettimeofday;
gettimeofday(&start, NULL);
在加载文件完成后使用;
gettimeofday(&end, NULL);
打印;
printf("加载数据库文件成功! 耗时: %.2lf s CPU耗时:%.2lf s\n", ((double)(end.tv_sec * 1000000 + end.tv_usec) - (start.tv_sec * 1000000 + start.tv_usec)) / 1000000,((double)(cpuend - cpustart)) / CLOCKS_PER_SEC);
4.使用makefile
因为这个程序调用了sqlite3库,所以在编译时需要加上 -lsqlite3,要是每次都加可能比较麻烦,所以我们创建makefile文件;
.PHONY:是一个伪指令,
a.out:select_world.c
gcc $^ -o $@ -lsqlite3
.PHONY:
clean:
rm a.out
5.如何将加载时间压缩到极致;
第一种方法:关闭磁盘写同步
PRAGMA synchronous = NORMAL;
第二种方法:开启事务
begin 和 commit;
第三种办法:使用预处理SQL语句机制实现提升数据库效率
今天就到这里啦!