用c简单编写一个电子词典的心得

学习了两个月c语言相关知识后写的一个项目,内容是一个网络电子词典,主要功能是为用户提供一个能实现查询用户注册,用户登陆和查询单词机查询历史记录等功能。本文从项目的角度进行了简单的描述。该系统是基于Linux系统下利用了网络的基本知识,及sqlite数据库的相关知识。这套系统采用的是tcp协议的并发服务器设计,可以满足多用户同时登录,用户登录后可以查询单词及历史记录,对于数据的存储我们采用的是sqlite数据库技术。

功能:

        1、登录:输入正确的账号和密码。

        2、注册:注册账号和密码,保存在数据库中,注册已经存在的账号会提示账号已经存在,并                注册失败。

        3、查询单词:通过数据库查询单词,如果有则返回单词注释,没有则返回无此单词。

        4、查询搜索历史:用time函数获取搜索时的时间,利用文件IO技术来保存搜索历史。

        5、退出。

运用技术点:

                 1、运用的是tcp协议进行网络通信,确保了传输的稳定性。

                 2、使用多进程来进行运行,提高运行效率,子进程负责登录,注册,查询等功能。

                 3、使用signal函数检测子进程改变状态,回收僵尸态子进程,释放子进程的资源。

                 4、传输数据包内容的时候同时传输数据包大小,可以解决粘包问题。

                 5、数据库中手动创建两个表,一个是要查询的单词及解释,另一个是保存账户和密                             码,方便实现不同的功能。                

                 6、 采用文件IO的方式创建一个文件来保存查询历史,利用time函数能获得查询时间 一                         起保存。
 

注:__attribute__((__packed__))表示取消结构体对齐
typedef struct
{
char _username[25];  //用户名
char _password[25];     //密码
} __attribute__((__packed__))user_t;   
 
typedef struct
{
int  type;        //判断要实现的对应功能
int  size;        //解决粘包问题
union
{
user_t  uinfo;    //用户信息
char    _word[100];
} content;
 
 
//客户端填词,服务端填写单词解释
#define word   			content._word
#define username 		content.uinfo._username
#define password       content.uinfo._passwd
}__attribute__((__packed__))mhead_t;
#ifndef _HEAD_H_
#define _HEAD_H_
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sqlite3.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
 
 
int do_register(int sockfd,sqlite3 *pdb,char *_username,char *_password);
int do_client(int sockfd,sqlite3 *pdb);
int do_login(int sockfd,sqlite3 *pdb,char *_username,char *_password);
 
//消息的类型
#define USER_REGISTER    10
#define USER_LOGIN       20
#define USER_WORD        30
#define USER_SUCCESS     40
#define USER_FAILURE     50
#define USER_HISTORY     60
 
//__attribute__((__packed__))
//作用:告诉编译器在编译的过程中取消优化对齐。
//      方便我们在发送和接收数据的时候一个字节一个字节的排列 
typedef struct
{
	char _username[25]; //用户名
	char _password[25]; //密码
}__attribute__((__packed__))user_t;
 
typedef struct
{
	int type;           //消息类型
	int size;           //消息大小
	union
	{
		user_t uinfo;	    //用户信息
		char   _word[1000]; 
	}content;
 
//客户端填单词,服务端填写单词解释
#define word         content._word
#define username     content.uinfo._username
#define password       content.uinfo._password
}__attribute__((__packed__))mhead_t;
 
//'\'表示多行链接上一行表示, #deifne ....do...while(0);
//表示封装成独立的语法单元,防止被语法错误。
//注意:'\'之后不要留空格,要不然编译会有警告
 
#define EXEC_SQL(db,sql,errmsg) do{\
	if(sqlite3_exec(db,sql,NULL,NULL,&errmsg) < 0)\
	{\
		fprintf(stderr,"sqlite3 execl [%s] error : %s.\n",sql,errmsg);\
		exit(EXIT_FAILURE);\
	}\
}while(0);
 
#endif
#include "head.h"
 
void signal_handler(int signum)
{
	waitpid(-1,NULL,WNOHANG);
	return;
}
int init_tcp(char *ip,char *port)
{
	int sockfd;
	struct sockaddr_in server_addr;
	
	if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0)
	{
		perror("Fail to socket");
		exit(EXIT_FAILURE);
	}
	bzero(&server_addr,sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port   = htons(atoi(port));
	server_addr.sin_addr.s_addr = inet_addr(ip);
 
	if(bind(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr)) < 0)
	{
		perror("Fail to bind");
		exit(EXIT_FAILURE);
	}
	
	listen(sockfd,5);
	printf("listen....\n");
	return sockfd;
}
 
//.server ip port db
//数据库中已经手动创建了2个表:user_table,word_table
//注:由于我们后面函数要传承,故这里的const应该去掉
int main(int argc, char *argv[])
{
	int pid;	
	sqlite3 *pdb;
	int listenfd,connect_fd;
	int addr_len = sizeof(struct sockaddr);
	struct sockaddr_in peer_addr;
 
	if(argc < 4)
	{
		fprintf(stderr,"Usage : %s ip port system.db.\n",argv[0]);
		exit(EXIT_FAILURE);
	}
 
	//探测子进程的改变状态,回收僵尸态子进程
	if(signal(SIGCHLD,signal_handler) == SIG_ERR)
	{
		perror("Fail to signal");
		exit(EXIT_FAILURE);
	}
 
	if(sqlite3_open(argv[3],&pdb) != SQLITE_OK)
	{
		fprintf(stderr,"sqlite3 open %s : %s.\n",argv[3],sqlite3_errmsg(pdb));
		exit(EXIT_FAILURE);
	}
 
	//初始化tcp连接,得到监听套接字
	listenfd = init_tcp(argv[1],argv[2]);
 
	//提取客户段的链接请求,创建子进程和客户端交互
	while(1)
	{
		if((connect_fd = accept(listenfd,(struct sockaddr *)&peer_addr,&addr_len)) < 0)
		{
			perror("Fail to accept");
			exit(EXIT_FAILURE);
		}
		
		if((pid = fork()) < 0)
		{
			perror("Fail to fork");
			exit(EXIT_FAILURE);
		}
 
		//创建子进程处理客户端的请求
		if(pid == 0){
			close(listenfd);
			do_client(connect_fd,pdb);
		}
 
		close(connect_fd);
	}
 
	exit(EXIT_SUCCESS);
}
#include "head.h"
//用户提示界面1
void help_info1()
{
	printf("\t-----------------------------------------------\n");
	printf("\t|               HENRY   在线辞典               |\n");
	printf("\t|版本:0.0.1                                    |\n");
	printf("\t|作者:我不是高科                               |\n");
	printf("\t|功能:                                         |\n");
	printf("\t|    [1] 登录                                  |\n");
	printf("\t|    [2] 注册                                  |\n");
	printf("\t|    [3] 退出                                  |\n");
	printf("\t|注意:用户只有登录成功后才能进入查单词界面     |\n");
	printf("\t------------------------------------------------\n");
	return;
}
 
//查询系统界面
void help_info2()
{
	printf("\t-----------------------------------------------\n");
	printf("\t|               HENRY   在线辞典               |\n");
	printf("\t|版本:0.0.1                                    |\n");
	printf("\t|作者:我不是高科                               |\n");
	printf("\t|功能:                                         |\n");
	printf("\t|    [1] 查单词                                |\n");
	printf("\t|    [2] 查询历史记录                          |\n");
	printf("\t|    [3] 退出查询系统                          |\n");
	printf("\t|注意:用户只有登录成功后才能进入查单词界面     |\n");
	printf("\t------------------------------------------------\n");
	return;
}
 
//用户输入指令,供大家选择
enum{
	LOGIN    = 1,  //登陆
	REGISTER = 2,  //注册
	QUIT     = 3,  //退出
	QUERY    = 1,  //查询单词
	HISTORY  = 2,  //查询历史
};
 
int init_tcp(char *ip,char *port)
{
	int sockfd;
	struct sockaddr_in server_addr;
 
	if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0)
	{
		perror("Fail to socket");	
		exit(EXIT_FAILURE);
	}
 
	bzero(&server_addr,sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(atoi(port));
	server_addr.sin_addr.s_addr = inet_addr(ip);
 
	if(connect(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr)) < 0)
	{
		perror("Fail to bind");	
		exit(EXIT_FAILURE);
	}
 
	return sockfd;
}
//查询单词
int do_query(int sockfd)
{
	int n = 0;
	int count = 0;
	char buf[1024] = {0};
	//定义发送的协议头
	mhead_t *head = (mhead_t *)buf;
 
	printf("\n您正在查询单词\n");
 
	head->type = USER_WORD;
	head->size = sizeof(mhead_t);
 
	printf("Input world : ");
	fgets(head->word,sizeof(head->word),stdin);
	head->username[strlen(head->username) - 1] = '\0';      //去掉回车
 
	//发给服务器端
	
	if(send(sockfd,buf,sizeof(mhead_t),0) < 0)
	{
		perror("Fail to send2");
		exit(EXIT_FAILURE);
	}
	
	bzero(&buf,sizeof(buf));
	while(1)
	{
		//接收数据,TCP是可靠的连接,若是数据
		//未完全接收的话,可以在接收
		n = recv(sockfd,buf + count,sizeof(mhead_t) - count,0);
 
		if(n < 0){
			perror("Fail to send3");
			exit(EXIT_FAILURE);
		}else if(n ==0 )
		{
			perror("Fail to send5");
			break;
		};
		//若是数据未发送完成,再次接收的时候可补充
		count += n;
		if(count == sizeof(mhead_t))
			break;
	}
 
	if(head->type == USER_SUCCESS)
	{
		printf("\n%s\n",head->word);	
	
		return 0;
	}else{
		printf("\nNo such word:\n");	
		return -1;
	}
 
}
 
//历史查询
int do_history(int sockfd)
{
	int n = 0;
	int count = 0;
	char buf[1024] = {0};
	//定义发送的协议头
	mhead_t *head = (mhead_t *)buf;
 
	printf("\n您正在查询记录\n");
 
	head->type = USER_HISTORY;
	head->size = sizeof(mhead_t);
	
	if(send(sockfd,buf,sizeof(mhead_t),0) < 0)
	{
		perror("Fail to send2");
		exit(EXIT_FAILURE);
	}
	
	bzero(&buf,sizeof(buf));
	while(1)
	{
		//接收数据,TCP是可靠的连接,若是数据
		//未完全接收的话,可以在接收
		n = recv(sockfd,buf + count,sizeof(mhead_t) - count,0);
 
		if(n < 0){
			perror("Fail to send3");
			exit(EXIT_FAILURE);
		}else if(n ==0 )
		{
			perror("Fail to send5");
			break;
		};
		//若是数据未发送完成,再次接收的时候可补充
		count += n;
		if(count == sizeof(mhead_t))
			break;
	}
 
	if(head->type == USER_SUCCESS)
	{
		printf("\n%s\n",head->word);	
	
		return 0;
	}else{
		printf("\nerror\n");	
		return -1;
	}
 
}
 
int do_task1(int sockfd)
{
	int cmd;
	while(1)
	{
		//提示界面帮助,用户选择
		help_info2();	
 
		printf("\n\n请选择>");
		scanf("%d",&cmd);
		//吃掉回车键
		getchar();
		switch(cmd)
		{
			//查询单词
			case QUERY:
				if(do_query(sockfd) < 0)         
					continue;
					break;
			//查询历史记录
			case HISTORY:	
				if(do_history(sockfd) < 0)
					continue;
					 break;
			//退出查询系统		 
			case QUIT:					 
				exit(EXIT_SUCCESS);
			default:
				printf("Unknow cmd.\n");
				continue;
		}
	}
	return 0;
}
 
 
//注册
int do_register(int sockfd)
{
	int n = 0;
	int count = 0;
	char buf[1024] = {0};
	//定义发送的协议头
	mhead_t *head = (mhead_t *)buf;
 
	printf("\n您正在注册,请输入用户名和密码\n");
 
	head->type = USER_REGISTER;
	head->size = sizeof(mhead_t);
 
	printf("Input username : ");
	fgets(head->username,sizeof(head->username),stdin);
	head->username[strlen(head->username) - 1] = '\0';      //去掉回车
 
	printf("Input password : ");
	fgets(head->password,sizeof(head->password),stdin);
	head->password[strlen(head->password) - 1] = '\0';
 
	//发给服务器端
	
	if(send(sockfd,buf,sizeof(mhead_t),0) < 0)
	{
		perror("Fail to send0");
		exit(EXIT_FAILURE);
	}
	
	bzero(&buf,sizeof(buf));
	while(1)
	{
		//接收数据,TCP是可靠的连接,若是数据
		//未完全接收的话,可以在接收
		n = recv(sockfd,buf + count,sizeof(mhead_t) - count,0);
 
		if(n <= 0){
			perror("Fail to sendi0");
			exit(EXIT_FAILURE);
		}
		//若是数据未发送完成,再次接收的时候可补充
		count += n;
		if(count == sizeof(mhead_t))
			break;
	}
 
	if(head->type == USER_SUCCESS)
	{
		printf("\n恭喜您,注册成功!\n");	
		return 0;
	}else{
		printf("\n很遗憾,这个用户名已经被其它用户注册过了,请重新注册");	
		return -1;
	}
 
}
 
 
int do_login(int sockfd)
{
	int n = 0;
	int count = 0;
	char buf[1024] = {0};
	//定义发送的协议头
	mhead_t *head = (mhead_t *)buf;
 
	printf("\n您正在登录,请输入用户名和密码\n");
 
	head->type = USER_LOGIN;
	head->size = sizeof(mhead_t);
 
	printf("Input username : ");
	fgets(head->username,sizeof(head->username),stdin);
	head->username[strlen(head->username) - 1] = '\0';      //去掉回车
 
	printf("Input password : ");
	fgets(head->password,sizeof(head->password),stdin);
	head->password[strlen(head->password) - 1] = '\0';
 
	//发给服务器端
	
	if(send(sockfd,buf,sizeof(mhead_t),0) < 0)
	{
		perror("Fail to send2");
		exit(EXIT_FAILURE);
	}
	
	bzero(&buf,sizeof(buf));
	while(1)
	{
		//接收数据,TCP是可靠的连接,若是数据
		//未完全接收的话,可以在接收
		n = recv(sockfd,buf + count,sizeof(mhead_t) - count,0);
 
		if(n < 0){
			perror("Fail to send3");
			exit(EXIT_FAILURE);
		}else if(n ==0 )
		{
			perror("Fail to send5");
			break;
		};
		//若是数据未发送完成,再次接收的时候可补充
		count += n;
		if(count == sizeof(mhead_t))
			break;
	}
 
	if(head->type == USER_SUCCESS)
	{
		printf("\n恭喜您,登录成功!\n");	
		do_task1( sockfd);
	
		return 0;
	}else{
		printf("\n登录失败,用户名或者密码错误!\n");	
		return -1;
	}
 
}
 
 
int do_task(int sockfd)
{
	int cmd;
	while(1)
	{
		//提示界面帮助,用户选择
		help_info1();	
 
		printf("\n\n请选择>");
		scanf("%d",&cmd);
		//吃掉回车键
		getchar();
		switch(cmd)
		{
			//用户注册,我们先来写注册的函数
			case REGISTER:
				if(do_register(sockfd) < 0)         //注册
					continue;
					break;
			//用户登陆
			case LOGIN:	
				if(do_login(sockfd) < 0)
					continue;
					 break;
			case QUIT:					 
				exit(EXIT_SUCCESS);
			default:
				printf("Unknow cmd.\n");
				continue;
		}
	}
	return 0;
}
 
 
 
//./client ip port 
//由于后面要传递参数,故这里的const省略
int main(int argc, char *argv[])
{
	int sockfd;	
	int addr_len = sizeof(struct sockaddr);
	struct sockaddr_in peer_addr;
 
	if(argc < 3)
	{
		fprintf(stderr,"Usage : %s argv[1] argv[2]\n",argv[0]);	
		exit(EXIT_FAILURE);
	}
 
	sockfd = init_tcp(argv[1],argv[2]);
 
	do_task(sockfd);
 
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 在Linux下,我们可以基于C语言实现一个简易的电子词典。以下是一个简单的实现示例: 首先,我们需要一个词典文件,可以是一个文本文件,其中包含词汇和其对应的解释。我们可以使用一个自定义的格式,例如每行包含一个词汇和其解释,用制表符或其他分隔符区分两者。 接下来,我们编写一个C程序,实现打开词典文件、读取查询词汇、查找对应解释并输出的功能。 首先,我们需要使用stdio.h头文件来实现文件读写功能,以及string.h头文件来处理字符串。 然后,我们定义一个函数,比如dict_search,用于接收用户输入的查询词汇,并在词典文件中查找对应的解释。 在函数内部,我们首先打开词典文件,可以使用fopen函数,接受词典文件路径和读取模式作为参数。 接着,我们将用户输入的查询词汇与词典文件中的每一行进行比较。可以使用fgets函数逐行读取词典文件,并使用strcmp函数比较输入的查询词汇和当前行的词汇。 如果找到匹配的词汇,我们可以使用strtok函数分割当前行,以获取词汇和解释,然后将解释输出给用户。 如果没有找到匹配的词汇,我们可以输出一个提示信息,告诉用户该词汇不在词典中。 最后,我们在主函数中接收用户输入的查询词汇,并调用dict_search函数进行查询和输出。 这只是一个简单电子词典实现示例,实际上,我们还可以添加更多功能,例如增加新的词汇、编辑和删除已存在的词汇等。实现这些功能需要进一步的编程和逻辑设计。 ### 回答2: 在Linux下基于C语言实现一个简易电子词典可以通过以下步骤进行: 1. 首先,需要创建一个字典文件,可以是文本文件格式,其中包含需要查询的单词和对应的解释。可以使用文本编辑器(如vi或gedit)创建一个名为dictionary.txt的文件,并在其中添加单词和解释,每个单词和解释一行。 2. 在C语言中,可以使用标准库函数来读取字典文件,并将其加载到内存中以便进行查询。 3. 创建一个函数来处理用户输入的查询词,并查找字典文件中是否存在该词。 4. 如果找到了该词,则将其解释打印到屏幕上;如果未找到,则提示用户词典中没有该词。 5. 可以使用循环来实现一个简单的命令行界面,以便用户能够进行多次查询,直到用户选择退出程序。 以下是一个简单的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_WORD_LEN 100 #define MAX_DEF_LEN 1000 void searchWord(char *word) { FILE *file = fopen("dictionary.txt", "r"); if (file == NULL) { printf("Unable to open dictionary file.\n"); return; } char line[MAX_DEF_LEN]; char currentWord[MAX_WORD_LEN]; int found = 0; while (fgets(line, sizeof(line), file)) { sscanf(line, "%s", currentWord); if (strcmp(currentWord, word) == 0) { found = 1; printf("Definition: %s\n", line + strlen(currentWord)); break; } } if (!found) { printf("Word not found in the dictionary.\n"); } fclose(file); } int main() { char word[MAX_WORD_LEN]; while (1) { printf("Enter a word to search for (or 'q' to quit): "); scanf("%s", word); if (strcmp(word, "q") == 0) { break; } searchWord(word); } return 0; } ``` 这是一个简单电子词典实现,用户可以输入要查询的单词,该程序将在字典文件中查找并显示单词的定义。用户输入“q”时程序退出。注意,这只是一个基本示例,实际应用中可能需要更复杂的功能和更高级的数据结构来提高性能和扩展性。 ### 回答3: 在Linux下使用C语言实现一个简易电子词典,可以按照以下步骤进行: 1. 使用C语言编写一个菜单界面,提供用户选择不同的功能,如添加词条、查找词条、删除词条和退出等选项。 2. 创建一个数据结构,用于存储词典中的词条。可以使用链表或散列表的形式进行存储,每个词条包含词语和对应的释义。 3. 实现添加词条功能。用户可以输入待添加的词语和对应的释义,程序将其添加到数据结构中。 4. 实现查找词条功能。用户可以输入待查找的词语,程序将根据输入的词语在数据结构中查找对应的释义,并将其显示给用户。 5. 实现删除词条功能。用户可以输入待删除的词语,程序将根据输入的词语在数据结构中找到对应的词条并删除。 6. 实现退出功能。用户选择退出程序后,程序将保存已经添加的词条并结束运行。 以上步骤仅为简易电子词典的实现思路,具体实现过程中还需要处理异常情况,例如输入错误的命令或词条不存在等。另外,可以考虑增加一些额外功能,如修改词条、显示所有词条等,以提升词典的实用性。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值