//直接上代码 \
C语言写数据库
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>   //断言函数的库
#include<errno.h>   //错误机制库

#define MAX_DATA 512
#define MAX_ROWS 100

struct Adress{
	int id;
	int set;
	char name[MAX_DATA];
	char email[MAX_ROWS];
};

struct Database{
	struct Adress rows[MAX_ROWS];
};

struct Connection{
	FILE* file;
	struct Database* db;
};

void die(const char* message){
	if(errno){         //发生错误时该error的变量值为1,可通过perror打印出错信息
		perror(message);    //和printf的作用差不多,只是它使用在错误的时候打印
	}else{
		printf("ERROR:%s\n",message);
	}
	
	exit(1);      //不正常程序退出
}

void Adress_print(struct Adress* addr){
	printf("%d %s %s\n",addr->id,addr->name,addr->email);
}

void Database_load(struct Connection* conn){

	//fread函数:将file指针指向的内容读取1个数据块,大小为sizeof那么大的数据块,给db指针; \
	返回数据块指定个数表示成功;反之读取失败。
	int rc=fread(conn->db,sizeof(struct Database),1,conn->file);   
	if(rc != 1)
		die("Failed to load database.");
}

struct Connection* Database_open(const char* filename,char mode){
	struct Connection* conn=malloc(sizeof(struct Connection));
	if(!conn)     //NULL的宏定义本质是0
		die("Memory error");
	
	conn->db=malloc(sizeof(struct Database));
	if(!conn)
		die("Memory error");
	
	if(mode == 'c'){
		conn->file =fopen(filename,"w");
	}else{
		conn->file =fopen(filename,"r+");    //读取已存在的文件,光标到开头
		
		if(conn->file){
			Database_load(conn);
		}
	}
	
	if(!conn->file)
		die("Failed to open the file");
	
	return conn;
}
//释放资源的时候,应该先放“子资源”,再放“父资源”;防止出现野地址(野内存,无法认领或访问的内存)
void Database_close(struct Connection* conn){
	if(conn){
		if(conn->file)
			fclose(conn->file);
		if(conn->db)
			free(conn->db);
		free(conn);
	}
}

void Database_write(struct Connection* conn){
	rewind(conn->file);    //rewind函数:文件I/O流的指针回归文件开头
	
	//将若干数据块写入指定文件,写入file文件指针
	int rc=fwrite(conn->db,sizeof(struct Database),1,conn->file);
	if(rc != 1)
		die("Failed to write database.");
	
	rc=fflush(conn->file);   //清除缓存I/O流
	if(rc == -1)
		die("Cannot flush database.");
}

void Database_create(struct Connection* conn){
	int i=0;
	for(i=0;i<MAX_ROWS;i++){
		struct Adress addr={.id=i,.set=0};
		conn->db->rows[i]=addr;   
	}
}

void Database_set(struct Connection* conn,int id,const char* name,const char* email){
	struct Adress* addr=&conn->db->rows[id];
	if(addr->set)
		die("Already set,delete it first");
	
	addr->set=1;
	char* res=strncpy(addr->name,name,MAX_DATA);
	if(!res)
		die("Name copy failed");
}

void Database_get(struct Connection* conn,int id){
	struct Adress* addr=&conn->db->rows[id];
	
	if(addr->set){
		Adress_print(addr);
	}else{
		die("ID is not set");
	}
}

void Database_delete(struct Connection* conn,int id){
	struct Adress addr={.id=id,.set=0};   //结构体的默认赋值方法,可以省略结构体名
	conn->db->rows[id]=addr;             //当结构体嵌套时候,应该搞清对应关系,避免指向再指向的问题
}

void Database_list(struct Connection* conn){
	int i=0;
	struct Database* db=conn->db;
	
	for(i=0;i<MAX_ROWS;i++){
		struct Adress* cur=&db->rows[i];
		
		if(cur->set)
			Adress_print(cur);
	}
}

int main(int argc,char* argv[]){
	if(argc<3)
		die("USAGE:ex17 <dbfile><action> [action params]");
	char* filename=argv[1];
	char action=argv[2][0];
	struct Connection* conn=Database_open(filename,action);
	int id=0;
	
	if(argc>3) id=atoi(argv[3]);    //将字符转换成int型
	if(id>MAX_ROWS) die("There`s not that many records.");
	
	switch(action){
		case 'c':
				Database_create(conn);
				Database_write(conn);
				break;
		case 'g':
				if(argc!=4)
					die("Need an id to get");
				Database_get(conn,id);
				break;
		case 's':
				if(argc!=6)
					die("Need an id ,name,email to set");
				
				Database_set(conn,id,argv[4],argv[5]);
				Database_write(conn);
				break;
		case 'd':
				if(argc!=4)
					die("Need an id to delete");
				
				Database_delete(conn,id);
				Database_write(conn);
				break;
		case 'l':
				Database_list(conn);
				break;
		default:die("Invalid action: c=create,g=get,s=set,d=del,l=list");
	}
	
	Database_close(conn);
	
	
	return 0;
}
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐