博客 / 詳情

返回

用C語言和文本文件實現一個簡單的,可保存的通訊錄

我們先思考一個通訊錄都有那些信息,很明顯通訊錄記錄的是人
人有哪些信息呢
這裏我就寫5個吧,分別是姓名,年齡,電話,性別,地址
然後我們把他們寫成一個結構體,最好定義在頭文件裏,這樣在使用的時候更方便
我們還可以把要使用的一些常用的或者要修改的常量定義成枚舉,後續有什麼要添加的,直接就能在枚舉中添加

//枚舉的一些關鍵常量
enum NUM
{
	NAME = 20, //名字
	TELE = 12,  //電話
	SEX = 10,  //性別
	ADDR = 100,  //地址
	ADD = 1,DEL = 2,SEARCH = 3,MODIFY = 4,SHOW = 5,SORT = 6,EXIT = 0,CLEAR =7,//菜單
	//ARRDATA = 100
	SIZE = 3,  //通訊錄初始容量大小
	SIZEADD = 2  //每次擴容的增加量
};
//人的信息
typedef struct peo
{
	char name[NAME];   //姓名
	int age;            //年齡
	char tele[TELE];  //電話
	char sex[SEX];    //性別
	char addr[ADDR];   //地址
}peo; 

這裏的枚舉內容我直接把參數都寫裏了,後續在思考的過程就不再改動了
接下來思考,光有一個人的信息是不夠的,無法完整表達一個通訊錄,當然,由於我們只是簡單實現,我們就只再記錄通訊錄的人員數量和通訊錄的容量
把他們簡單封裝成一個結構體

typedef struct contact
{
	peo* data;          //人的相關數據
	size_t count;     //成員數量
	size_t capacity;   //通訊錄容量
}contact;

這裏我都是進行了重命名了的,方便後續的使用
這些都是頭文件裏面要用的,還有一些庫函數,我就在這裏提前定義了

#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>
#include<errno.h>

然後我們寫主函數
我們考慮通訊錄運行一定要有菜單
然後我們用switch語句寫分支結構
我們需要用户主動輸入選擇,所以我們還需要一個變量接收,這個變量還可以用於switch語句的判斷
菜單我們可以單獨封裝一個函數
可以實現增加聯繫人,刪除聯繫人,查找聯繫人,修改聯繫人,顯示聯繫人,按名字排序聯繫人,清空聯繫人,和退出保存,將數據保存到文件中,把這些功能都封裝成函數
還得創建一個結構體變量用於存放通訊錄的數據
還需要一個函數對結構體進行初始化,並把保存在文件中的信息放入到結構體中
於是主函數如下

#include"contact.h"
static void menu()   //菜單函數
{
	printf("****************************************\n");
	printf("****************************************\n");
	printf("****************************************\n");
	printf("***       1.add      2.del        ******\n");
	printf("***       3.search   4.modify     ******\n");
	printf("***       5.show     6.sort       ******\n");
	printf("***       0.exit     7.clear      ******\n");
	printf("****************************************\n");
	printf("****************************************\n");
	printf("****************************************\n");
}
int main()
{
	int input = 0;        //存放菜單選擇輸入的值
	contact con;     //創建存放通訊錄數據的結構體
	init_contact(&con);//初始化結構體
	do
	{
		menu();
		printf("請輸入要進行的功能\n");
		scanf("%d", &input);        //菜單的選擇輸入
		switch (input)            //菜單選擇的判斷邏輯
		{
		case ADD:
			add_contact(&con);       //添加函數
			break;
		case DEL:
			del_contact(&con);       //刪除函數
			break;
		case SEARCH:
			search_contact(&con);    //查找函數
			break;
		case MODIFY:
			modify_contact(&con);    //修改函數
			break; 
		case SHOW:
			show_contact(&con);        //顯示函數
			break;
		case SORT:
			sort_contact(&con);       //排序函數
			break;
		case CLEAR:
			clear_contact(&con);   //清空函數
			break;
		case EXIT:
			save_contact(&con);       //保存函數
			destroycontact(&con);      //推出後內存的銷燬
			printf("程序退出\n");
			break;
		default:
			printf("選擇錯誤,請重新選擇\n");//其他邏輯判斷
			break;
		}
	} while (input)//do while的循環判斷
	return 0;
}

主函數別忘了引用頭文件
接下來我們封裝函數
把函數單獨放在一個源文件

#include"contact.h"
//初始化函數
//對於初始化我們不僅要初始化結構體內容,還要把之前保存在文件中的信息放入到結構體中,當然還涉及到一個動態擴容的問題,我們的結構體初始設計的是有容量的,如果裏面放的信息多了,還要涉及到擴容的問題,
這些都要設計函數
static void expandCapacity(contact* con)  //擴容
{
	assert(con);  //斷言防止空指針
	if (con->capacity == con->count)   //容量判斷
	{
		void* p = 0;
		p = realloc(con->data, (con->capacity + SIZEADD) * sizeof(peo));//追加信息容量
		if (NULL == p) //報錯判斷
		{
			printf("expandCapacity::%s\n", strerror(errno));
			return;
		}
		con->capacity += SIZEADD;//修改容量標記
		con->data = (peo*)p;       //修改指針位置(其實沒變)
		printf("增容成功\n");
	}
}
static int read_conact(contact* con)   //讀取文件中保存的通訊錄信息
{
	static void expandCapacity(contact * con);   //函數聲明
	FILE* p = fopen("contact.txt", "r");      //打開文件
	if (p == NULL)                          //報錯判斷
	{
		return 1;
	}
	peo a = { 0 };
	while (fread(&a, sizeof(peo), 1, p) == 1)//循環提取信息,這裏取決於保存函數的寫法
	{
		expandCapacity(con);  //擴容
		con->data[con->count] = a;//賦值
		(con->count)++;//成員計數加一
	}
	fclose(p);
	p = NULL;//關閉文件,指針空置
	return 0;
}
void init_contact(contact* con)    //初始化函數
{
	static int read_conact(contact * con);//函數調用聲明
	assert(con);////斷言防止空指針
	con->count = 0;//初始化人數
	void* p = 0;//內存指針
	p = calloc(SIZE, sizeof(peo));//創建內存
	if (NULL == con->data)//報錯判斷
	{
		printf("init_contact:calloc:%s\n", strerror(errno));
		return;
	}
	con->data = (peo*)p;//初始化人信息指針
	con->capacity = SIZE;//初始化容量
	if (read_conact(con) == 1)//讀取之前的數據
	{
		perror("讀取舊數據失敗");//問題判斷
		return;
	}
}

//初始化我們已經完成了
//接下來我們寫添加函數
void add_contact(contact* con)
{
	static void expandCapacity(contact * con);  //函數聲明
	assert(con);//斷言防止空指針
	expandCapacity(con);//擴容判斷

	printf("請輸入姓名\n");
	scanf("%s", (con->data+con->count)->name);
	printf("請輸入年齡\n");
	scanf("%d", &((con->data + con->count)->age));
	printf("請輸入電話\n");
	scanf("%s", con->data[con->count].tele);
	printf("請輸入性別\n");
	scanf("%s", con->data[con->count].sex);
	printf("請輸入地址\n");
	scanf("%s", con->data[con->count].addr);//這些都是信息是輸入
	con->count++;//計數加一
	printf("增加成功\n");
}


//接下來我們寫顯示函數
void show_contact(contact* con)
{
	assert(con);//斷言防止空指針
	int i = 0;
	printf("%-20s\t%-5s\t%5s\t%12s\t%30s\n", "姓名", "年齡", "電話", "性別", "地址");
	//顯示時方便觀察,打印一個標籤
	for (i = 0; i < con->count; i++)//循環打印人員信息
	{
		printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n",
			con->data[i].name,
			con->data[i].age,
			con->data[i].tele,
			con->data[i].sex,
			con->data[i].addr);
	}
}

//查找函數
//查找函數不僅要能查找,還要能顯示,我們分成兩個函數,因為刪除也需要查找
//查找部分
static int find_by_name(const contact* con,const char* arr)  //依據名字查找
{
	assert(con && arr);//斷言防止空指針
	int i = 0;
	for (i = 0; i < con->count; i++)//循環遍歷查找
	{
		if (0 == strcmp(con->data[i].name, arr))
		{
			return i;//找到返回下標
		}
	}
	return -1;//找不到返回-1
}

//查找
void search_contact(contact* con)
{
	static int find_by_name(const contact * con, const char* arr);//函數聲明
	assert(con);//斷言防止空指針
	if (con->count == 0)//無聯繫人情況
	{
		printf("無聯繫人可查找\n");
		return;
	}
	printf("請輸入要查找的人的姓名\n");
	char arr[NAME] = { 0 };
	scanf("%s", arr);
	int a = find_by_name(con, arr);//套用查找函數
	if (a == -1)
	{
		printf("要查找的人不存在\n");
		return;
	}
	printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "姓名", "年齡", "電話", "性別", "地址");
	printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n",
			con->data[a].name,
			con->data[a].age,
			con->data[a].tele,
			con->data[a].sex,
			con->data[a].addr);//顯示部分
}



//刪除
void del_contact(contact* con)
{
	static int find_by_name(const contact * con, const char* arr);//函數聲明
	assert(con);//斷言防止空指針
	char arr[NAME] = { 0 };
	if (con->count == 0)//無聯繫人情況
	{
		printf("無聯繫人可刪除\n");
		return;
	}
	printf("請輸入要刪除人的名字\n");
	scanf("%s", arr);
	int a = find_by_name(con,arr);//函數回調
	if (a == -1)
	{
		printf("要刪除的人不存在\n");
		return;
	}
	for (; a < con->count - 1; a++)//刪除本質就是把後面的信息提前,覆蓋掉這個下標的信息
	//當然也還有其他的寫法
	{
		con->data[a] = con->data[a + 1];
	}
	con->count--;
	printf("刪除成功\n");
}


//修改函數
//修改也要先找

void modify_contact(contact* con)
{
	static int find_by_name(const contact * con, const char* arr);//函數聲明
	assert(con);//斷言防止空指針
	if (con->count == 0)
	{
		printf("無聯繫人可修改\n");//沒有的情況判斷
		return;
	}
	printf("請輸入要修改的人的姓名\n");
	char arr[NAME] = { 0 };
	scanf("%s", arr);
	int a = find_by_name(con, arr);//調用函數
	if (a == -1)
	{
		printf("要修改的人不存在\n");
		return;
	}
	printf("請輸入修改姓名\n");
	scanf("%s", con->data[a].name);
	printf("請輸入修改年齡\n");
	scanf("%d", &(con->data[a].age));
	printf("請輸入修改電話\n");
	scanf("%s", con->data[a].tele);
	printf("請輸入修改性別\n");
	scanf("%s", con->data[a].sex);
	printf("請輸入修改地址\n");
	scanf("%s", con->data[a].addr);
	printf("修改成功\n");//信息的修改
}


//排序函數
//這裏我使用了qsort
static int cmp_peo_by_name(const void* e1, const void* e2)//qsort調用的排序函數
{
	return strcmp(((const peo*)e1)->name, ((const peo*)e2)->name);
}

void sort_contact(contact* con)//本體
{
	static int cmp_peo_by_name(const void* e1, const void* e2);//函數調用
	assert(con);//斷言
	if (con->count == 0)
	{
		printf("無聯繫人可排序\n");//沒人的情況
		return;
	}
	printf("正在按姓名排序\n");
	qsort(con->data, con->count, sizeof(peo), cmp_peo_by_name);//qsort排序
	printf("排序成功\n");
	show_contact(con);//這裏不聲明是因為這些函數都會在頭文件中聲明,但內些靜態函數不會
}

//銷燬,保存,清空
void destroycontact(contact* con)//釋放內存
{
	assert(con);
	free(con->data);
	con->data = NULL;
}


void save_contact(const contact* con)//保存信息
{
	assert(con);
	FILE* p = fopen("contact.txt", "wb");
	if (p == NULL)
	{
		perror("保存失敗:save_contact:fopen");
		return;
	}
	fwrite(con->data, sizeof(peo), con->count, p);
	fclose(p);
	p = NULL;
}

void clear_contact(contact* con)//清空文件信息和當前結構體信息
{
	assert(con);
	FILE* p = fopen("contact.txt", "w");
	if (p == NULL)
		return;
	fclose(p);
	p = NULL;
	con->count = 0;
	memset(con->data,0, sizeof(peo));
	printf("已全部清除\n");
}

最後我們再在頭文件中聲明一下這些函數,為了調用

//初始化通訊錄
void init_contact(contact* con);

//增加聯繫人
void add_contact(contact* con);

//顯示
void show_contact(contact* con);

//刪除
void del_contact(contact* con);

//查找
void search_contact(contact* con);

//修改
void modify_contact(contact* con);

//排序
void sort_contact(contact* con);

//退出後內存銷燬
void destroycontact(contact* con);

//退出後的保存
void save_contact(const contact*con);

//清空通訊錄
void clear_contact(contact*con);

靜態函數不用聲明,主要和一些商業習慣有關

好了,這樣就結束了,比較簡陋,旨在練手,大佬勿噴

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.