C语言的文件操作与通讯录写入文件

文章目录

        • 一、为什么使用文件
        • 二、什么是文件
          • 2.1 程序文件
          • 2.2 数据文件
          • 2.3 文件名
        • 三、文件的打开和关闭
          • 3.1文件指针
          • 3.2 打开和关闭文件
          • 3.3 fopen函数的一些使用细节
        • 四、文件读写的方式
          • 4.1顺序读写
            • 4.1.1 fputc 向输出流写入一个字符
            • 4.1.2 fgetc 从输入流stream获取(读取)一个字符
            • 4.1.3 fputs 向输出流写入字符串
            • 4.1.4 fgets 从输入流中读取字符串
            • 4.1.5 fprintf 向输出流进行格式化写入
            • 4.1.6 fscanf 从输入流里进行格式化输入
            • 4.1.7 fwrite 向文件流写入二进制
            • 4.1.8 fread 从文件流读入二进制文件
          • 4.2 对比一组函数
          • 4.3 sprintf和sscanf的使用实例
          • 4.4 文件的随机读写
            • 4.4.1 fseek
            • 4.4.2 ftell
            • 4.4.3 rewind
            • 4.4.4 练习
        • 五、文本文件和二进制文件
        • 六、文件读取结束的判定
          • 6.1 判断文件读取结束
          • 6.2文件读取结束的原因的一些判断
        • 七、文件缓冲区
        • 八、通讯库写入文件

一、为什么使用文件

  以我们写的通讯录为例,关闭了程序后再打开这个程序内容就不见了。因为我们的数据放在内存里,结束操作就还给操作系统了,为了改进这些事情,我们可以把这些数据放到硬盘里。

  通讯录里数据,能不能也放在文件中,存储的磁盘上,做到持久化的效果。

  我们只要把信息写在文件中就可以了。

二、什么是文件

  在程序设计中,我们一般把文件分为程序文件和数据文件。

2.1 程序文件

包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境 后缀为.exe)。

2.2 数据文件

文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文 件,或者输出内容的文件。

本章讨论数据文件。

在以前各章所处理数据的输入输出都是以终端为对象的,即从终端的键盘输入数据,运行结果显示到显示器上。

2.3 文件名

一个文件要有一个唯一的文件标识,以便用户识别和引用。

文件名包含3部分:文件路径+文件名主干+文件后缀

例如: c:\code\test.txt

为了方便起见,文件标识常被称为文件名。

文件名可以不带后缀。

三、文件的打开和关闭

操作文件的步骤:

  • 打开文件
  • 读/写文件
  • 关闭文件
3.1文件指针

文件信息区—结构体struct FILE f
我们操作文件就是在操作文件信息区

FILE这个结构的具体内容

struct _iobuf {
        char *_ptr;
        int   _cnt;
        char *_base;
        int   _flag;
        int   _file;
        int   _charbuf;
        int   _bufsiz;
        char *_tmpfname;
       };
typedef struct _iobuf FILE;
//VS2013

不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异。

每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息, 使用者不必关心细节。

一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。

FILE* pf

定义pf是一个指向FILE类型数据的指针变量。

可以使pf指向某个文件的文件信息区(是一个结构体变量)。

通过该文件信息区中的信息就能够访问该文件,通过文件指针可以访问某个文件信息区。
也就是说,通过文件指针变量能够找到与它关联 的文件。

3.2 打开和关闭文件

打开文件—使用fopen函数,如果打开文件失败会返回空指针。

//打开文件
FILE * fopen ( const char * filename, const char * mode );
//filename 文件名 字符串形式
//mode 打开方式
//关闭文件
int fclose ( FILE * stream );//要关闭那个文件 把那个文件指针传过去就行了。
3.3 fopen函数的一些使用细节

C语言的文件操作与通讯录写入文件_第1张图片

四、文件读写的方式

操作文本文件的方法:

1.顺序读写。

2.随机读写。

4.1顺序读写

其主要使用的函数有:

C语言的文件操作与通讯录写入文件_第2张图片

什么是流呢?

流:高度抽象的概念

流可以帮你怎么写到具体的外部设备里头去,外部设备如文件、屏幕、网络、光盘、软盘,我们只要关心怎么写到流去。

FILE*一个文件流,printf 屏幕(输出流),scanf键盘(输入流)。

任何一个C语言程序,运行起来默认打开了三种流:标准输出流stdout、标准输入流stdin、标准错误流stderr,它们的类型FILE*

所以你想让fputc往屏幕上打印东西也行,fputc(‘b’,stdout);就行

4.1.1 fputc 向输出流写入一个字符
//原型
int fputc( int c, FILE *stream );
//参数c 要向流写的字符
//stream 被写入的流

//使用
int main()
{
	//打开文件
	//FILE* pf = fopen("C:\\Users\\lenovo\\Desktop\\data.txt", "r");//转义字符
	//这样写算绝对路径
	//直接写data.txt称为相对路径
	FILE* pf = fopen("C:\\Users\\lenovo\\Desktop\\data1.txt", "w");
	//"w" 打开一个文件来写 如果原本里面有内容 会销毁文件内容
	//如果本来没有这个文件,会创建一个文件
	if (pf == NULL)
	{
		perror("fopen");//打印错误信息
		return -1;
	}
	//读/写文件
	fputc('b', pf);//一次写一个字符
	fputc('i', pf);
	fputc('t', pf);
    //可以打开文件查看效果
    
    //也可以向标准输出(屏幕)写入字符
	fputc('b', stdout);
	fputc('i', stdout);
	fputc('t', stdout);
    //此时相当于putchar函数

	//关闭文件
	fclose(pf);
	pf = NULL;


	return 0;
}
4.1.2 fgetc 从输入流stream获取(读取)一个字符
//原型
int fgetc( FILE *stream );
//参数 输出流。
#include 
int main()
{
    FILE* pf=fopen("C:\\Users\\lenovo\\Desktop\\data.txt", "r");
    int ch = fgetc(pf);
	printf("%c", ch);
	ch = fgetc(pf);
	printf("%c", ch);
	ch = fgetc(pf);
	printf("%c", ch);
    //读一个,指针往后移动一下,
    //从标准输入stdin读取
    int ch = fgetc(stdin);
	printf("%c", ch);
	ch = fgetc(stdin);
	printf("%c", ch);
	ch = fgetc(stdin);
	printf("%c", ch);
    fclose(pf);
    pf=NULL;
    return 0;
}
//此时相当于getchar函数
4.1.3 fputs 向输出流写入字符串
//文本行输入函数 fputs
//原型
int fputs( const char *string, FILE *stream );
//string 你要写的字符串
//stream 写到一个输出流上
#include 
int main()
{
	FILE* pf = fopen("C:\\Users\\lenovo\\Desktop\\data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return -1;
	}
	fputs("hello bit!\n", pf);
	fputs("hello laoxuejie\n", pf);
	//要换行 自己加\n

	fclose(pf);
	pf == NULL;

	return 0;
}
4.1.4 fgets 从输入流中读取字符串
//原型
char *fgets( char *string, int n, FILE *stream );
//参数 string 存储读到的字符串
//n 读取的字符数(算上\0的)
//stream 输入流
//最多读一行
#include 
int main()
{
    FILE* pf=fopen("C:\\Users\\lenovo\\Desktop\\data.txt","r+");
    if(pf==NULL)
    {
        perror("fopen");
        return -1;
    }
    char arr[20]={0};
    fputs("hello bit!\n", pf);
	fputs("hello laoxuejie\n", pf);
    fgets(arr,5,pf);
    printf("%s",arr);
    fgets(arr,5,pf);
    printf("%s",arr);
    fclose(pf);
    pf=NULL;
    
    return 0;
}
4.1.5 fprintf 向输出流进行格式化写入

格式化输出函数;fprintf;所有输出流

//原型
int fprintf( FILE *stream, const char *format [, argument ]...);
//可以观察到除了第一个参数是一个输出流意外,后面那个参数是和printf一样的

#include 
struct S{
     int x;
     double y;
 }s1={1,3.14};
int main()
{
    FILE* pf=fopen("C:\\Users\\lenovo\\Desktop\\data.txt","w");
    if(pf==NULL)
    {
        perror("fopen");
        return -1;
    }
    fprintf(pf,"%d %lf",s1.x,s1.y);
    
    fclose(pf);
    pf=NULL;
    return 0;
}
4.1.6 fscanf 从输入流里进行格式化输入

格式化输入函数 ;fscanf ;所有输入流

int fscanf( FILE *stream, const char *format [, argument ]... );
//一样可以观察到 第一个参数是一个输入流 后面的参数和scanf的参数一样

//接续上面的fprintf
#include 
struct S{
    int x;
    double y;
}s1={1,3.14};

int main()
{
    struct S s2={0};
    FILE* pf=fopen("C:\\Users\\lenovo\\Desktop\\data.txt","r");
    if(pf==NULL)
    {
        perror("fopen");
        return -1;
    }
    fscanf(pf,"%d %lf",&(s2.x),&(s2.y));
    printf("%d %lf",s2.x,s2.y);
    
    fclose(pf);
    pf=NULL;
    return 0;
}

4.1.7 fwrite 向文件流写入二进制
//原型
size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );
//参数 
//buffer 指向要写入东西的首地址 
//size 组成要写入的东西的单元的大小
//count 组成要写入东西的单元的数量
//stream 输出文件流
//返回值 实际写入的单元的个数
#include 
struct S{
    int x;
    double y;
    char a[25];
}s1={1,3.14,"hello my brother"};
int main()
{
    FILE* pf=fopen("C:\\Users\\lenovo\\Desktop\\data.txt","wb");
    if(pf==NULL)
    {
        perror("fopen");
        return -1;
    }
    struct S* p=&s1;
    fwrite(p,sizeof(struct S),1,pf);
    fclose(pf);
    pf=NULL;
    return 0;
}
4.1.8 fread 从文件流读入二进制文件
//原型
size_t fread( void *buffer, size_t size, size_t count, FILE *stream );
//参数 
//buffer 存储读入数据的首地址
//size 读入元素的大小
//count 读入元素的个数
//stream 输入文件流

#include 
struct S{
    int x;
    double y;
    char a[25];
}s1={1,3.14,"hello my brother"},s2={0};
int main()
{
    FILE* pf=fopen("C:\\Users\\lenovo\\Desktop\\data3.txt","rb");
    if(pf==NULL)
    {
        perror("fopen");
        return -1;
    }
    struct S* p2=&s2;
    fread(p2,sizeof(struct S),1,pf);
    printf("%d %lf %s",s2.x,s2.y,s2.a);
    
    fclose(pf);
    pf=NULL;
    return 0;
}
4.2 对比一组函数

scanf/fscanf/sscanf printf/fprintf/sprintf

  • scanf:从标准输入读取格式化输入

  • fscanf:从所有的输入流读取格式化输入

  • sscanf:从字符串中读取一个结构化的数据

  • printf:把格式化数据输出到标准输出(屏幕)

  • fprintf:把格式化数据输出到所有输出流(屏幕/文件)上

  • sprintf:把格式化的数据转化成对应的字符串放到一个字符串里头

4.3 sprintf和sscanf的使用实例
//原型
int sprintf( char *buffer, const char *format [, argument] ... );
//参数 buffer 数据打印到的字符串
//后面和printf的参数一样,就是把一个格式化的数据打印到这个字符串里头
int sscanf( const char *buffer, const char *format [, argument ] ... );
//buffer 待提取的字符串
//后面的参数和scanf一样 就是从一个字符串中提取出格式化的数据。


#include 
struct S {
	int x;
	double y;
	char a[25];
}s1 = { 1,3.14,"hellow" }, s2 = {0};

int main()
{
	struct S* p1 = &s1;
	char arr[100] = { 0 };
	sprintf(arr, "%d %lf %s", s1.x, s1.y, s1.a);
	printf("%s\n", arr);
	sscanf(arr, "%d %lf %s", &(s2.x), &(s2.y), &(s2.a));
	printf("%d %lf %s\n", s2.x, s2.y, s2.a);
	return 0;
}
4.4 文件的随机读写

ps:文件指针在读完一个东西以后都会指向次东西下一个位置。

4.4.1 fseek

根据文件指针的位置和偏移量来定位文件指针。

int fseek ( FILE * stream, long int offset, int origin );
//返回值:如果定位成功,返回0;否则返回非零值
//参数 stream 文件指针
//offset 偏移量 
//orgin 起始位置 orgin参数取值:
//SEEK_CUR 文件指针的当前位置
//SEEK_END 文件结束的位置
//SEEK_SET 文件起始的位置
4.4.2 ftell

返回文件指针相对于起始位置的偏移量

long int ftell ( FILE * stream );
4.4.3 rewind

让文件指针的位置回到文件的起始位置

void rewind ( FILE * stream );
4.4.4 练习
#include 
int main()
{
	FILE* pf=fopen("C:\\Users\\lenovo\\Desktop\\data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return -1;
	}
	//随机读写
	fseek(pf, 2, SEEK_SET);
	char ch=fgetc(pf);
	//文件指针自动移动到了下一位d 所以后面是-2
	printf("%c\n", ch);
	fseek(pf, -2, SEEK_CUR);
	ch = fgetc(pf);
	printf("%c\n", ch);
	int b=ftell(pf);
	printf("%d", b);
	rewind(pf);
	ch = fgetc(pf);
	printf("%c", ch);
	fclose(pf);
	pf = NULL;
	return 0;
}

五、文本文件和二进制文件

  根据数据的组织形式,数据文件被称为文本文件或者二进制文件。

   数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件

   如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件

  一个数据在内存中是怎么存储的呢?

  字符一律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。

  如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节),而 二进制形式输出,则在磁盘上只占4个字节。

测试代码:

#include 
int main()
{
	FILE* pf = fopen("test.txt", "wb");
	if (pf == NULL)
	{
		perror("fopen");
		return -1;
	}
	int a = 10000;
	fwrite(&a, sizeof(int), 1, pf);

	fclose(pf);
	pf = NULL;

	return 0;
}

  然后用vs源文件添加现有项,选择text.txt,然后右键text.txt,打开方式,以二进制打开,然后就能看到我们把10000以二进制存到文件里的情况了。

六、文件读取结束的判定

6.1 判断文件读取结束

  如果使用fgetc读取文件,可以判断返回值是否是EOF,来判断文件读取是否结束。

  如果使用fgets读取文件,可以判断返回值是否是NULL来判断文件读取是否结束。

  如果使用fscanf读取文件,返回值是实际读取的field的个数,如果文件读取结束,返回值是EOF,可以判断返回值是不是EOF来判断文件读取是否结束。

  如果使用fread读取文件,可以判断返回值是否小于给定要读入的元素单元个数来判断读取是否结束。

6.2文件读取结束的原因的一些判断

feof不是用来判断文件是否读取结束的,是用来判断读取结束的原因是什么的。

牢记:在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束。 而是应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束。

先判断文件读取是否结束:

  1. 文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )
    • 例如: fgetc 判断是否为 EOF .
    • fgets 判断返回值是否为 NULL .
  2. 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。
    • 例如: fread判断返回值是否小于实际要读的个数。

feof返回非0值,是遇到文件末尾结束了,返回0,是其他原因导致读取结束。

用途:用在文件读取结束后来判断是不是因为遇到文件末尾结束

ferror根据返回值判断是不是因为遇到错误而结束了,如果返回0,则没有错误发生,发生错误则返回非0值。

用途:用在文件读取结束后判断是不是遇到错误后读取结束

#include 
#include 
int main(void)
{
    int c; // 注意:int,非char,要求处理EOF
    FILE* fp = fopen("test.txt", "r");
    if(!fp) {
        perror("File opening failed");
        return EXIT_FAILURE;
   }
 //fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF
    while ((c = fgetc(fp)) != EOF) // 标准C I/O读取文件循环
   { 
       putchar(c);
   }
    //判断是什么原因结束的
    if (ferror(fp))//相当于if(ferror(fp)!=0) ferror函数返回非0值代表发生错误
        puts("I/O error when reading");
    else if (feof(fp))//相当于if(feof(fp)!=0) feof函数返回非0值代表是因为文件读到末尾结束了
        puts("End of file reached successfully");
    fclose(fp);
}

七、文件缓冲区

  ANSIC 标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序 中每一个正在使用的文件开辟一块“文件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲区,装 满缓冲区后才一起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根据C编译系统决定的,想让系统把缓冲区的东西送出去要么用fflush函数或者用fclose刷新缓冲区。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BhViRj0N-1632805170136)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20210927200143504.png)]

//验证缓冲区存在
#include 
#include 
int main()
{
 FILE*pf = fopen("test.txt", "w");
 fputs("abcdef", pf);//先将代码放在输出缓冲区
 printf("睡眠10秒-已经写数据了,打开test.txt文件,发现文件没有内容\n");
 Sleep(10000);
 printf("刷新缓冲区\n");
 fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到文件(磁盘)
 //注:fflush 在高版本的VS上不能使用了
 printf("再睡眠10秒-此时,再次打开test.txt文件,文件有内容了\n");
 Sleep(10000);
 fclose(pf);
 //注:fclose在关闭文件的时候,也会刷新缓冲区 睡十秒钟就是为了验证不是fclose刷新的缓冲区。
 pf = NULL;
 return 0;
}
//我们之间的写入都是fclose才写入的 

  这里可以得出一个结论: 因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。 如果不做,可能导致读写文件的问题。

八、通讯库写入文件

  作为练习,我们利用C语言的文件操作函数,解决之前通讯录的内容关闭通讯录后就消失的问题。
  具体思路是这样的,
  首先我们把之前add函数中负责扩容的部分单独提取出来封装为checkandexpansion函数,因为负责扩容的部分利用fi语句判断了当前是否需要扩容,这是为了读入文件做的准备。
  然后,为了每次退出通讯录后我们的通讯录数据不消失,我们在exitable函数中利用fwrite函数把通讯录内容以二进制写到一个文件里去,那么自然,我们也需要打开通讯录的时候自动读入那个文件,每次打开通讯录我们都会运行Initcontact函数,因此我们可以在Initcontact函数中利用fread函数读入二进制文件。
  这里有一个难点,我们并不知道这个二进制文件里头有多少个通讯录成员,可我们是动态扩容的,无法确定当前容量能不能撑得下这些通讯录成员啊?我们可以利用fread函数的返回值,其返回值表示从二进制文件中读入了多少个元素,我们利用一个while循环,每次只读一个通讯录成员信息进来,循环条件为fread函数的返回值不为0(也就是还没读完),那如果容量不够怎么办?利用checkandexpansion函数,每次执行时,先执行checkandexpansion函数,如果当前容量不足则扩容,然后再利用fread函数从二进制文件中读入通讯录成员的信息。
具体代码如下:

contact.h
#ifndef define _CONTACT_H_
#define _CONTACT_H_

#include 
#include 
#include 

//一些方便改的数
#define MAXNAME 20
#define MAXSEX 5
#define MAXTEL 20
#define MAXDOOR 30
#define MAX 1000
#define DEFAULTSIZE 3

//人的信息
typedef struct {
	char name[MAXNAME];
	char sex[MAXSEX];
	int age;
	char tel[MAXTEL];
	char address[MAXDOOR];
}person;

通讯录静态版本
//typedef struct {
//	person perinfo[MAX];
//	int sz;//当前通讯录中有效的人的个数
//}Contact;
//通讯录动态版本
typedef struct {
	person* perinfo;
	int sz;//通讯录中当前有的有效元素个数
	int capcity;//通讯录当前的容量
}Contact;

enum Option {
	EXIT,
	ADD,
	DELETE,
	FIND,
	CHANGE,
	SHOW,
	CLEAR,
	SORT,
};
int checkandexpansion(Contact* p);

int Initcontact(Contact* p);

void add(Contact* p);

void show(Contact* p);

void find(Contact* p);

void contactdelete(Contact* p);

void contactchange(Contact* p);

void contactclear(Contact* p);

void contactsort(Contact* p);

int exitable(Contact* p);




#endif


main.c

#include "contact.h"

void menu()
{
	printf("*******************************************\n");
	printf("************这是一个通讯录设备*************\n");
	printf("*******************************************\n");
	printf("********输入对应数字启动对应功能***********\n");
	printf("*******************************************\n");
	printf("******1.ADD******************2.DELETE******\n");
	printf("*******************************************\n");
	printf("******3.FIND*****************4.CHANGE******\n");
	printf("*******************************************\n");
	printf("******5.SHOW*****************6.CLEAR*******\n");
	printf("*******************************************\n");
	printf("******7.SORT*****************0.EXIT********\n");
	printf("*******************************************\n");
}
int main()
{
	int input = 0;
	//void(*pf[])(Contact*) = { exitable,add,contactdelete,
	//find,contactchange,show,contactclear,contactsort };
	Contact contact;
	int a = Initcontact(&contact);
	int b;
	if (a == -1)
	{
		return -1;
	}
	do {
		menu();
		scanf("%d", &input);
		//(*pf[input])(&contact);
		switch (input)
		{
		case ADD: add(&contact); break;
		case DELETE:contactdelete(&contact); break;
		case FIND:find(&contact); break;
		case CHANGE:contactchange(&contact); break;
		case SHOW:show(&contact); break;
		case CLEAR:contactclear(&contact); break;
		case SORT:contactsort(&contact); break;
		case EXIT: b = exitable(&contact);
			if (b == -1)
			{
				input = 5;
			}
			break;
		default: printf("输入错误 请重新输入\n");
		}
	} while (input != 0);

	return 0;
}

contact.c

#include "contact.h"

//静态的版本
//void Initcontact(Contact* p)
//{
//	p->sz = 0;
//	memset(&p->perinfo, 0, MAX * sizeof(person));
//}

int checkandexpansion(Contact* p)
{
	//增加容量
	if (p->capcity == p->sz)
	{
		person* ptr = (person*)realloc(p->perinfo, (p->capcity + 2) * sizeof(person));
		if (ptr == NULL)
		{
			return -1;
		}
		else
		{
			p->perinfo = ptr;
			p->capcity += 2;
			printf("增容成功!\n");
			return 0;
		}
	}
	return 1;
}



//动态版本
int Initcontact(Contact* p)
{
	p->sz = 0;
	p->perinfo = (person*)malloc(DEFAULTSIZE * sizeof(person));
	p->capcity = DEFAULTSIZE;
	FILE* pf = fopen("C:\\Users\\lenovo\\Desktop\\contact.txt", "rb");
	if (pf == NULL)
	{
		printf("打开文件失败\n");
		return -1;
	}
	person temp = { 0 };
	while (fread(&temp, sizeof(person), 1, pf) != 0)
	{
		checkandexpansion(p);
		p->perinfo[p->sz] = temp;
		p->sz++;
	}
	fclose(pf);
	pf = NULL;
	return 0;
}
//静态的add
//void add(Contact* p)
//{
//	if (p->sz == MAX)
//	{
//		printf("通讯录满了\n");
//	}
//	else
//	{
//		printf("请输入姓名\n");
//		scanf("%s", p->perinfo[p->sz].name);
//		printf("请输入性别\n");
//		scanf("%s", p->perinfo[p->sz].sex);
//		printf("请输入年龄\n");
//		scanf("%d", &(p->perinfo[p->sz].age));
//		printf("请输入电话\n");
//		scanf("%s", p->perinfo[p->sz].tel);
//		printf("请输入住址\n");
//		scanf("%s", p->perinfo[p->sz].address);
//		p->sz++;
//	}
//}
// 
//动态版本
void add(Contact* p)
{
	int ret = 1;
	//增加容量
	ret = checkandexpansion(p);
	if (ret != -1)
	{
		//录入新增人的信息
		printf("请输入姓名\n");
		scanf("%s", p->perinfo[p->sz].name);
		printf("请输入性别\n");
		scanf("%s", p->perinfo[p->sz].sex);
		printf("请输入年龄\n");
		scanf("%d", &(p->perinfo[p->sz].age));
		printf("请输入电话\n");
		scanf("%s", p->perinfo[p->sz].tel);
		printf("请输入住址\n");
		scanf("%s", p->perinfo[p->sz].address);
		p->sz++;
		printf("增加成功!\n");
	}
	else
	{
		printf("由于内存不足原因添加失败\n");
	}
}

void show(Contact* p)
{
	printf("%-20s\t%-5s\t%-s\t%-14s\t%-30s\n", "name", "sex", "age", "tel", "address");
	for (int i = 0; i < p->sz; i++)
	{
		printf("%-20s\t%-5s\t%-d\t%-14s\t%-30s\n", p->perinfo[i].name, p->perinfo[i].sex, p->perinfo[i].age, p->perinfo[i].tel, p->perinfo[i].address);
	}
}

void find(Contact* p)
{
	int a = 0;
	printf("如果你是要根据电话号查找,请输入1,如果要根据姓名查找,请输入0\n");
	scanf("%d", &a);
	if (a == 0)
	{
		char c[MAXNAME];
		printf("请输入查找姓名\n");
		scanf("%s", c);
		int ret = 0;
		for (int i = 0; i < p->sz; i++)
		{
			if (strcmp(c, p->perinfo[i].name) == 0)
			{
				printf("查找成功\n对应信息为\n");
				ret = 1;
				printf("%-20s\t%-5s\t%-d\t%-14s\t%-30s\n", p->perinfo[i].name, p->perinfo[i].sex, p->perinfo[i].age, p->perinfo[i].tel, p->perinfo[i].address);
				break;
			}
		}
		if (ret == 0)
		{
			printf("电话簿中查无此人,请核对此人的姓名是否输入正确\n");
		}
	}
	else if (a == 1)
	{
		int ret = 0;
		printf("请输入待查找的电话号码\n");
		char c[MAXTEL];
		scanf("%s", c);
		for (int i = 0; i < p->sz; i++)
		{
			if (strcmp(c, p->perinfo[i].tel) == 0)
			{
				printf("查找成功\n对应信息为\n");
				ret = 1;
				printf("%-20s\t%-5s\t%-d\t%-14s\t%-30s\n", p->perinfo[i].name, p->perinfo[i].sex, p->perinfo[i].age, p->perinfo[i].tel, p->perinfo[i].address);
				break;
			}
		}
		if (ret == 0)
		{
			printf("电话簿中查无此人,请核对此人的姓名是否输入正确\n");
		}
	}
	else
	{
		printf("输入错误,即将回到初始界面\n");
	}
}

void contactdelete(Contact* p)
{
	int a = 0;
	printf("如果你是要根据电话号删除,请输入1,如果要根据姓名删除,请输入0\n");
	scanf("%d", &a);
	if (a == 0)
	{
		char c[MAXNAME];
		printf("请输入要删除的姓名\n");
		scanf("%s", c);
		int ret = 0;
		int i = 0;
		for (i = 0; i < p->sz; i++)
		{
			if (strcmp(c, p->perinfo[i].name) == 0)
			{
				ret = 1;
				break;
			}
		}
		if (ret == 0)
		{
			printf("电话簿中查无此人,请核对此人的姓名是否输入正确\n");
		}
		else
		{
			for (int j = i; j < p->sz; j++)
			{
				p->perinfo[j] = p->perinfo[j + 1];
			}
			p->sz--;
			printf("删除成功\n");
		}
	}
	else if (a == 1)
	{
		int ret = 0;
		printf("请输入你要删除的电话号码\n");
		char c[MAXTEL];
		scanf("%s", c);
		for (int i = 0; i < p->sz; i++)
		{
			if (strcmp(c, p->perinfo[i].tel) == 0)
			{
				int j = i;
				for (j = i; i < p->sz; j++)
				{
					p->perinfo[j] = p->perinfo[j + 1];
				}
				p->sz--;
				printf("删除成功\n");
				ret = 1;
				break;
			}
		}
		if (ret == 0)
		{
			printf("电话簿中查无此人,请核对此人的电话号码是否输入正确\n");
		}
	}
	else
	{
		printf("输入错误,即将回到初始界面\n");
	}
}

void contactchange(Contact* p)
{
	char c[MAXNAME];
	printf("请输入要待修改单位的姓名\n");
	scanf("%s", c);
	int ret = 0;
	for (int i = 0; i < p->sz; i++)
	{
		if (strcmp(c, p->perinfo[i].name) == 0)
		{
			int change[5] = { 0 };
			printf("是否需要修改姓名?需要修改姓名请输入1,否则输入0\n");
			scanf("%d", &change[0]);
			if (change[0] == 1)
			{
				printf("请输入新姓名\n");
				scanf("%s", p->perinfo[i].name);
			}
			printf("是否需要修改性别?需要修改性别请输入1,否则输入0\n");
			scanf("%d", &change[1]);
			if (change[1] == 1)
			{
				printf("请输入正确性别\n");
				scanf("%s", p->perinfo[i].sex);
			}
			printf("是否需要修改年龄?需要修改年龄请输入1,否则输入0\n");
			scanf("%d", &change[2]);
			if (change[2] == 1)
			{
				printf("请输入新年龄\n");
				scanf("%d", &(p->perinfo[i].age));
			}
			printf("是否需要修改电话号码?需要修改电话号码请输入1,否则输入0\n");
			scanf("%d", &change[3]);
			if (change[3] == 1)
			{
				printf("请输入新电话号码\n");
				scanf("%s", p->perinfo[i].tel);
			}
			printf("是否需要修改住址?需要修改住址请输入1,否则输入0\n");
			scanf("%d", &change[4]);
			if (change[4] == 1)
			{
				printf("请输入新住址\n");
				scanf("%s", p->perinfo[i].address);
			}
			ret = 1;
			printf("修改成功!\n");
			printf("此人的新联系信息为:\n");
			printf("%-20s\t%-5s\t%-d\t%-14s\t%-30s\n", p->perinfo[i].name, p->perinfo[i].sex, p->perinfo[i].age, p->perinfo[i].tel, p->perinfo[i].address);
			break;
		}
	}
	if (ret == 0)
	{
		printf("电话簿中查无此人,请核对此人的姓名是否输入正确\n");
	}
}

//静态版本
//void contactclear(Contact* p)
//{
//	Initcontact(p);
//}
//动态版本
void contactclear(Contact* p)
{
	memset(p->perinfo, 0, (p->sz) * sizeof(person));
	p->sz = 0;
	printf("清除成功!\n");
}

void contactsort(Contact* p)
{
	int i, j;
	char temp1[MAXNAME];
	char temp2[MAXSEX];
	int temp3;
	char temp4[MAXTEL];
	char temp5[MAXDOOR];
	for (i = 0; i < p->sz; i++)
	{
		for (j = 0; j < p->sz - 1 - i; j++)
		{
			if (strcmp(p->perinfo[j].name, p->perinfo[j + 1].name) > 0)
			{
				strcpy(temp1, p->perinfo[j].name);
				strcpy(temp2, p->perinfo[j].sex);
				temp3 = p->perinfo[j].age;
				strcpy(temp4, p->perinfo[j].tel);
				strcpy(temp5, p->perinfo[j].address);

				strcpy(p->perinfo[j].name, p->perinfo[j + 1].name);
				strcpy(p->perinfo[j].sex, p->perinfo[j + 1].sex);
				p->perinfo[j].age = p->perinfo[j + 1].age;
				strcpy(p->perinfo[j].tel, p->perinfo[j + 1].tel);
				strcpy(p->perinfo[j].address, p->perinfo[j + 1].address);

				strcpy(p->perinfo[j + 1].name, temp1);
				strcpy(p->perinfo[j + 1].sex, temp2);
				p->perinfo[j + 1].age = temp3;
				strcpy(p->perinfo[j + 1].tel, temp4);
				strcpy(p->perinfo[j + 1].address, temp5);
			}
		}
	}
	printf("排序成功!新的通讯录信息如下!\n");
	show(p);
}
int exitable(Contact* p)
{
	FILE* pf = fopen("C:\\Users\\lenovo\\Desktop\\contact.txt", "wb");
	if (pf == NULL)
	{
		printf("存储文件失败 请重新退出\n");
		return -1;
	}
	for (int i = 0; i < p->sz; i++)
	{
		fwrite(&(p->perinfo[i]), sizeof(person), 1, pf);
	}
	fclose(pf);
	pf = NULL;
	free(p->perinfo);
	p->perinfo = NULL;
	p->sz = 0;
	p->capcity = 0;
	printf("销毁成功!\n");
	printf("退出\n");
	return 0;
}

你可能感兴趣的:(C/C++学习,c语言)