📄 通讯录.c
字号:
/*
*File: 通讯录.c
*--------------------------
*本程序采用结构数组方式编写,目前可以存储1000条信息,每条信息中包含6项子信息。
*程序在运行时会要求用户输入文件名,然后把文件中的信息读取到数组(内存)中。
*因此用户在程序运行时对记录所做的任何修改均是对数组元素内容的的修改,这些修改
*只有在用户执行保存指令或者退出程序的时候才会保存到文件中。
*文件中的记录采用从0开始的编码方式,即每条记录均与相同下标的数组元素对应。
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MaxElement 1000
#define Namelength 25
/*类型定义*/
typedef enum{false, true}bool;
typedef struct{
int n;
char *name;
char *number;
char *flatnum;
char *cellphone;
char *qqnum;
char *address;
}*oneRecord;
typedef struct{
oneRecord classmates[MaxElement];
int matesnum;
}*records;
/*函数声明*/
void AddRecord(records db);
oneRecord AddToRecord(int n);
void PrintRecord(FILE *infile, records db);
void GiveInstruction(int num, char *filename);
void DeleteRecord(records db);
void ChangeRecord(records db);
void SearchRecord(records db);
void *GetBlock(size_t nbytes);
void UpdateRecord(char *filename, FILE *infile, records db);
void Guide(void);
FILE *OPENFile(char *filename);
void ScanRecord(FILE *infile, records db);
void RangeRecord(records db);
/*主程序*/
int main()
{
Guide();
system ("pause");
return 0;
}
/*
*Function: Guide
*Usage: Guide();
*----------------------------------
*这个函数是本程序的主要框架,在用户输入文件名后,
*函数会给出程序的指令代码,并根据用户的选择来作出反应
*目前本程序共有8项功能。
*/
void Guide(void)
{
FILE *infile, *outfile; //声明两个变量以区分读写
records db;
char *line, *string, *newname;
int code;
string = (char *)GetBlock(100 * sizeof(char));
printf("请输入要创建或打开的文件名:");
gets(string);
infile = outfile = OPENFile(string);
db = (records)GetBlock(sizeof *db);
db->matesnum = 0;
line = GetBlock(100 * sizeof(char));
if(fgets(line, 100, infile) == NULL){ //判断文件是否为新文件
fprintf(outfile, "%s\n", "姓名: 学号: 宿舍: 手机: QQ: 住址: ");
}else{
ScanRecord(infile, db);
}
while(true){
rewind(infile); //因用户要使用的功能未知,故使用文件指针复位返回初始状态
rewind(outfile);
GiveInstruction(db->matesnum, string);
printf("请输入命令代号:");
scanf("%d", &code);
if(code == 9) { //当用户要退出程序时,自动保存记录,然后退出
UpdateRecord(string, outfile, db);
break;
}
switch(code){ //功能选择
case 1:
AddRecord(db);
break;
case 2:
DeleteRecord(db);
break;
case 3:
SearchRecord(db);
break;
case 4:
PrintRecord(infile, db);
break;
case 5:
ChangeRecord(db);
break;
case 6:
RangeRecord(db);
break;
case 7:
UpdateRecord(string, outfile, db);
printf("保存完毕。\n");
infile = outfile = OPENFile(string);
fgets(line, 100, infile);
ScanRecord(infile, db);
break;
case 8:
printf("请输入新文件名(带扩展名):");
newname = (char *)GetBlock(Namelength * sizeof(char));
scanf("%24s", newname);
getchar();
fclose(infile);
fclose(outfile);
if(rename(string, newname) != 0){
printf("文件重命名失败。\n");
infile = outfile = OPENFile(string);
break;
}
string = newname;
infile = outfile = OPENFile(string);
printf("文件重命名成功。\n");
break;
default:
printf("错误指令,请重新输入.\n");
break;
}
}
fclose(infile); //关闭文件
fclose(outfile);
}
/*
*Function: 0PENFile
*Usage: infile = OPENFile(filename);
*--------------------------------------
*这个函数从用户处获得文件名后将其打开
*若文件不存在则会创建一个新文件
*如果无法打开或创建文件,函数会报错,并退出程序
*/
FILE *OPENFile(char *filename)
{
FILE *file;
file = fopen(filename, "a+");
if(file == NULL){
printf("无法打开文件!\n");
system ("pause");
exit (0);
}
return (file);
}
/*
*Function: AddRecord
*Usage: AddRecord(db, db->matesnum);
*--------------------------------------
*这个函数可以往记录表中添加记录
*用户可以选择添加的位置,并可重复调用该函数
*以达到添加多条记录的目的
*/
void AddRecord(records db)
{
int answer, i;
oneRecord result;
printf("目前菜单:增加记录。确定增加请输入1,返回上一级菜单请输入0:");
while(true){
scanf("%d", &answer);
getchar();
if(answer == 0 || answer == 1) break;
printf("输入错误!请重新输入:");
}
if(!answer) return; //当用户输入0时
printf("是否添加到最后(是 1/否 0)?");
while(true){
scanf("%d", &answer);
getchar();
if(answer == 0 || answer == 1) break;
printf("输入错误!请重新输入:");
}
if(answer){
result = AddToRecord(db->matesnum);
printf("输入成功!\n");
db->classmates[db->matesnum] = result;
db->matesnum++;
}else{
printf("是否添加到开始(是 1/否 0)?");
while(true){
scanf("%d", &answer);
getchar();
if(answer == 0 || answer == 1) break;
printf("输入错误!请重新输入:");
}
if(answer){ //如果用户输入1
result = AddToRecord(0);
printf("输入成功!\n");
for(i = 0; i < db->matesnum; i++){
db->classmates[i]->n++;
}
for(i = db->matesnum; i > 0; i--){
db->classmates[i] = db->classmates[i - 1];
}
db->classmates[0] = result;
db->matesnum++;
}else{
printf("添加为第几条?");
scanf("%d", &answer);
getchar();
result = AddToRecord(answer);
for(i = answer; i < db->matesnum; i++){ //将从answer开始的记录的编号后移1个单位
db->classmates[i]->n++;
}
for(i = db->matesnum; i > answer; i--){ //记录指针后移
db->classmates[i] = db->classmates[i - 1];
}
db->classmates[answer] = result;
db->matesnum++;
}
}
printf("是否继续添加(是 1/否 0)?");
while(true){
scanf("%d", &answer);
getchar();
if(answer == 0 || answer == 1) break;
printf("输入错误!请重新输入:");
}
if(answer) AddRecord(db);
}
/*
*Function: AddToRecord
*Usage: result = AddToRecord(n);
*------------------------------------
*这个函数在用户要增加记录时调用,添加一条记录
*用户输入完毕后,函数返回该记录的指针
*/
oneRecord AddToRecord(int n)
{
oneRecord emp;
printf("请在一行中依次输入\"姓名\"\"学号\"\"宿舍\"\"手机\"\"QQ号\"\"住址\"\n");
printf("如该项信息空缺,请输入“无”而不是以空格代替。每项信息中不能有空格,两项信息之间以空格分隔。\n");
emp = (oneRecord)GetBlock(sizeof *emp);
emp->name = (char *)GetBlock(Namelength * sizeof(char));
emp->number = (char *)GetBlock(Namelength * sizeof(char));
emp->flatnum = (char *)GetBlock(Namelength * sizeof(char));
emp->cellphone = (char *)GetBlock(Namelength * sizeof(char));
emp->qqnum = (char *)GetBlock(Namelength * sizeof(char));
emp->address = (char *)GetBlock(Namelength * sizeof(char));
emp->n = n;
scanf("%6s %7s %8s %11s %11s %20s", emp->name, emp->number, emp->flatnum, emp->cellphone, emp->qqnum, emp->address);
getchar();
return (emp);
}
/*
*Function: PrintRecord
*Usage: PrintRecord(db);
*------------------------------------
*这个函数用于将记录显示在屏幕上
*用户可以根据需要选择显示部分或全部记录
*当用户选择显示部分记录时,若输入的起始终止编号相同,则只显示一条记录
*/
void PrintRecord(FILE *infile, records db)
{
int i = 0, start, end, answer;
char *title;
printf("目前菜单:显示记录。显示部分请输入0,显示全部请输入1,返回上一级菜单请输入2:");
while(true){
scanf("%d", &answer);
getchar();
if(answer == 0 || answer == 1 || answer == 2) break;
printf("输入错误!请重新输入:");
}
if(answer == 2) return;
if(db->matesnum == 0){
printf("文件中没有记录。\n");
return;
}
if(!answer){ //用户输入0时,选择显示部分功能
printf("请输入显示范围:\n");
while(true){
printf("请输入起始标号:");
scanf("%d", &start);
getchar();
printf("请输入终止标号:");
scanf("%d", &end);
getchar();
if(start <= end && end < db->matesnum) break;
printf("指令错误!请重新输入\n");
}
rewind(infile); //文件指针复位
title = (char *)malloc(100 * sizeof(char));
fgets(title, 100, infile);
printf("%s", title); //显示文件头,如“姓名”等
for(i = start; i <= end; i++){
printf("%-4d.%-6s %-7s %-8s %-11s %-11s %-20s\n", db->classmates[i]->n,db->classmates[i]->name,db->classmates[i]->number,db->classmates[i]->flatnum, db->classmates[i]->cellphone, db->classmates[i]->qqnum,db->classmates[i]->address);
}
printf("%d条记录显示完成。\n",end - start + 1);
}else{
rewind(infile);
title = (char *)malloc(100 * sizeof(char));
fgets(title, 100, infile);
printf("%s", title);
for(i = 0; i < db->matesnum; i++){
printf("%-4d.%-6s %-7s %-8s %-11s %-11s %-20s\n", db->classmates[i]->n,db->classmates[i]->name,db->classmates[i]->number,db->classmates[i]->flatnum, db->classmates[i]->cellphone, db->classmates[i]->qqnum,db->classmates[i]->address);
}
printf("%d条记录显示完成。\n", db->matesnum);
}
}
/*
*Function: GetBlock
*Usage: temp = (char *)GetBlock(n * sizeof(char));
*------------------------------------------------------
*这个函数用于从堆中动态分配内存
*/
void *GetBlock(size_t nbytes)
{
void *result;
result = malloc(nbytes);
if(result == NULL){
printf("申请内存失败!\n");
system ("pause");
exit (0);
}
return (result);
}
/*
*Function: ScanRecord
*Usage: ScanRecord(infile, db);
*---------------------------------
*这个函数将文件中的记录信息扫描到程序
*运行时声明的数组中,便于快速更改
*如果文件的格式有误,函数会报错并退出程序
*/
void ScanRecord(FILE *infile, records db)
{
oneRecord temp;
int i = 0, nscan;
char termch;
while(true){
temp = (oneRecord)GetBlock(sizeof *temp);
temp->name = (char *)GetBlock(Namelength * sizeof(char));
temp->number = (char *)GetBlock(Namelength * sizeof(char));
temp->flatnum = (char *)GetBlock(Namelength * sizeof(char));
temp->cellphone = (char *)GetBlock(Namelength * sizeof(char));
temp->qqnum = (char *)GetBlock(Namelength * sizeof(char));
temp->address = (char *)GetBlock(Namelength * sizeof(char));
nscan = fscanf(infile, "%d .%6s %7s %8s %11s %11s %20s%c", &(temp->n), temp->name, temp->number, temp->flatnum, temp->cellphone, temp->qqnum, temp->address, &termch);
if(nscan == EOF) break;
if(temp->n < 0 || temp->n >= MaxElement){ //当记录的编号超出范围时
printf("文件中记录格式有误,读取失败!\n");
system ("pause");
exit(1);
}
db->classmates[temp->n] = temp;
i++;
}
db->matesnum = i;
}
/*
*Function: UpdateRecord
*Usage: UpdateRecord(string, infile, db);
*-----------------------------------------
*这个函数用于更新文件,即将修改后的记录信息写入到文件中
*当这个函数在用户选择了保存记录功能后被调用时,它会新建
*一个临时文件,然后把记录写入临时文件中,最后以原文件名
*将临时文件重命名,并以之替换原文件
*/
void UpdateRecord(char *filename, FILE *infile, records db)
{
int i;
char *temname;
FILE *outfile;
temname = tmpnam(NULL);
outfile = OPENFile(temname);
fprintf(outfile, "%s\n", " 姓名: 学号: 宿舍: 手机: QQ: 住址: ");
for(i = 0; i < db->matesnum; i++){
fprintf(outfile, "%-4d.%-6s %-7s %-8s %-11s %-11s %-20s\n", db->classmates[i]->n,db->classmates[i]->name,db->classmates[i]->number,db->classmates[i]->flatnum, db->classmates[i]->cellphone, db->classmates[i]->qqnum,db->classmates[i]->address);
fflush(NULL);
}
fclose(infile);
fclose(outfile);
if(remove(filename) != 0 || rename(temname, filename) != 0){
printf("无法重命名临时文件!\n");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -