文件操作相关知识点(每一点都超级详细)

之前介绍过通讯录静态版本和动态内存开辟版本,这两个版本的通讯录运行过程中,通讯录数据是存放在内存中,当程序退出的时候,通讯录的数据自然就不存在了。每次重新运行数据,都得重新添加数据,这样操作起来就比较冗余。

因此我们就想找到一种新方法,只有在我们删除数据的时候,数据在不复存在,让数据持久化。我们一般将数据放在磁盘文件中或者存放在数据库中。

今天我就来介绍让数据持久化的一种方法,通过使用文件将数据直接存放在电脑的硬盘上,使数据持久化。

用一张图来总结文件操作:

文件操作相关知识点(每一点都超级详细)_第1张图片

目录

1、什么是文件

1.1 程序文件

1.2 数据文件

1.3 文件名

2、文件的打开和关闭

2.1 文件指针

2.2 文件的打开和关闭

3、文件的顺序读写 

3.1、fgetc和fputc:

3.2、fgets 和 fputs :

3.3、fscanf 和 fprintf

3.4 、fread 和 fwrite

4.文件的随机读写



1、什么是文件

 从文件功能上来分,文件可分为程序文件数据文件

1.1 程序文件

  • 包括 程序文件(后缀为.c,如test.c)、目标文件(后缀为.obj)和 可执行文件(windows环境下后缀为.exe);

1.2 数据文件

  •  存放着程序运行时读写的数据的文件。本篇博客我们讨论的是数据文件。

1.3 文件名

  • 文件名(也称文件标识)包含三个部分: 文件路径+文件名主干+文件后缀

        例如:C : \ code \ test.txt      文件路径(C : \ code \) 文件名主干(test) 文件后缀(.txt)


2、文件的打开和关闭

2.1 文件指针

每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字、文件状态及文件当前的位置) 这些信息均保存在一个结构体变量中的,该结构体类型是由系统声明的,取名为 FILE

2.2 文件的打开和关闭

文件在读写之前应该先打开文件,在使用之后关闭文件。

打开文件的同时,会返回一个FILE*类型的指针,需要用一个同样是FILE*类型的指针来接受,因此可以通过文件指针变量能够找出与它相关联的文件。 

打开文件和关闭文件的函数原型:

    //打开文件
	FILE * fopen(const char *filename, const char *mode);
	//关闭文件
	int fclose(FILE* stream);

我们打开文件的方式(形参mode)可以有很多种,我就列举几个常用的:

文件使用方式 含义 如果指定文件不存在
“r”(只读) 为了输入数据,打开一个已经存在的文本文件 出错
“w”(只写) 为了输出数据,打开一个文本文件 建立一个新的文件
“a”(追加) 向文本文件尾添加数据 建立一个新的文件
“rb”(只读) 为了输入数据,打开一个二进制文件 出错
“wb”(只写) 为了输出数据,打开一个二进制文件 建立一个新的文件

注意:

  • 以“w”的形式打开文件,如果文件中已有内容,则内容会被清空。
  • 注意这是对文件的操作,像“r”是为了向.c文件中输入数据而打开文件,“w”是为了将.c文件的数据输入到文件中而打开文件。这里动词后面接的宾语是数据文件,读+文件 / 写+文件。
/*fopen fclose example*/

int main()
{
	//以写的形式打开文件
	FILE*pfile = fopen("my_file", "w");   //这个是相对路径
    //文件操作
	if (pfile != NULL)
	{
		fputs("hello world", pfile);
	}
    //关闭文件
	fclose(pfile);
	pfile = NULL;
	return 0;
}

补充一个知识点: 绝对路径相对路径

相对路径: 如果我们在test.c中对文件读/写,且这个文件与test.c在同一大文件下,我们写fopen中形参filename的时候只需要写 文件名主干+文件后缀

举个例子:

文件操作相关知识点(每一点都超级详细)_第2张图片

我们在contact.c中进行了文件操作,数据文件为contact.txt,而contact.txt与contact.c在同一路径底下,因此我们filename写的是相对路径。

绝对路径:读/写的文件与test.c不在同一大文件下,我们写fopen中形参filename的时候需要写全文件名,也就是  文件路径+文件主干+文件后缀,并且 ‘ / ’ 要写成 “ // ”

举个例子:对桌面上的文件进行操作

文件操作相关知识点(每一点都超级详细)_第3张图片文件操作相关知识点(每一点都超级详细)_第4张图片

我们右击点开文件属性,复制 位置 的内容 C:\Users\lenovo\Desktop,然后将‘ / ’ 替换成 “ // ”,再补上 文件主干 和 文件后缀 ,变成 C:\\Users\\lenovo\\Desktop\\contact.c


3、文件的顺序读写 

功能 函数名 适用于
字符输入函数 fgetc 所有输入流
字符输出函数 fputc 所有输出流
文本行输入函数 fgets 所有输入流
文本行输出函数 fputs 所有输出流
格式化输入函数 fscanf 所有输入流
格式化输出函数 fprintf 所有输出流
二进制输入 fread 文件流
二进制输出 fwrite 文件流

常见的流:

  • 文件流
  • 标准输入流stdin(也就是从键盘输入)
  • 标准输出流stdout(输出到屏幕上)
  • 标准错误流stderr(输出到屏幕上)

 只有文件流需要我们自己打开,而后三个是在C程序运行起来后,就默认打开了。

接下来我举些例子来看看这些函数如何使用:

3.1、fgetc和fputc:

fgetc原型:

int fgetc(FILE * stream);

fputc原型:

int fputc(int character,FILE* stream);

 character 是输入字符的ASCII码值

/* fgetc example*/

int main()
{
	FILE * pfile;
	char ch;
	//对文件只读
	pfile = fopen("data.txt", "r");
	if (pfile == NULL)
	{
		perror("fopen");
		return;
	}
	//从文件中读取数据
	while ((ch = fgetc(pfile)) != EOF)
	{
		printf("%c", ch);
	}
	printf("\n");
	fclose(pfile);
    pfile=NULL;
	//从键盘获得值
	printf("%c", fgetc(stdin));
}
/* fputc example*/

int main()
{
	FILE * pfile;
	char ch;
	//对文件只写
	pfile = fopen("data.txt", "w");
	if (pfile == NULL)
	{
		perror("fopen");
		return;
	}
	//将数据写入文件中
	for (char i = 'a'; i <= 'z'; i++)
	{
		ch = i;
		fputc(ch, pfile);
	}
	fputc('\n', pfile);
	fclose(pfile);
    pfile=NULL;
	//将值输入到屏幕上
	fputc('h', stdout);
}

3.2、fgets 和 fputs :

fgets的原型:

char * fgets(char * str,int num,FILE* stream);

先定义一个字符数组 str[A],把数据传到数组里面,num的最大值为A,但实际只会传A-1个字符进去,剩下一个放‘\0’ 。

fputs的原型:

int fputs(const char * str,FILE*stream);

 第一个参数可以是数组的数组名,也可以直接是字符串。

/* fgets example*/

int main()
{
	FILE * pfile;
	char a[26];
	//对文件只写
	pfile = fopen("data.txt", "r");
	if (pfile == NULL)
	{
		perror("fopen");
		return;
	}
	//将数据写入数组中
	fgets(a, 26, pfile);
	puts(a);
	fclose(pfile);
	pfile = NULL;
}

fputs的使用方法跟fputc差不多,我就不再赘述了。

3.3、fscanf 和 fprintf

fscanf和scanf很像,fprintf和printf很像,其函数原型无非是多了一个参数,也就是fscanf和fprintf非要通过文件流才能实现硬盘和内存的连接:

int scanf(格式);
int fscanf(FILE* stream,格式);
int printf(格式);
int fprintf(FILE* stream,格式);

举个例子:

struct s
{
	char name[20];
    char sex[3];
	int age;
};

/* fscanf example */
int main()
{
	struct s s1= { 0 };
	FILE * pread = fopen("data.txt", "r");
	if (pread == NULL)
	{
		return;
	}
	//把文件的内容输入到程序(从磁盘到内存)
	fscanf(pread, "%s%s%d", s1.name, s1.sex, &(s1.age));
    //把屏幕上的内容输入到程序
    fscanf(stdin,"%s%s%d", s1.name, s1.sex, &(s1.age));
    //把结构体内容输出到屏幕上
	printf("%-5s %-3s %-2d", s1.name, s1.sex, s1.age);
	fclose(pread);
}
struct s
{
	char name[20];
    char sex[3];
	int age;
};

/* fprintf example */
int main()
{
	struct s s1= { 0 };
	FILE * pwrite = fopen("data.txt", "w");
	if (pwrite == NULL)
	{
		return;
	}
	scanf("%s%s%d", s1.name, s1.sex, &(s1.age));
	//从程序中的结构体读取数据输出到文件中(从内存到磁盘)
	fprintf(pwrite, "%-5s %-3s %-2d", s1.name, s1.sex, s1.age);
    //从程序中的结构体读取数据输出到屏幕上
    fprintf(stdout, "%-5s %-3s %-2d", s1.name, s1.sex, s1.age);
	fclose(pwrite);
	pwrite = NULL;
}

这里还得补充一对函数:sscanf和sprintf   原型如下:

int sscanf(const char *a, 格式);
int sprintf(char *a,格式);

sscanf的功能是把格式化的数据转化成字符串放到a中,sprintf的功能是把字符串转化成格式化的数据,返回的是成功转化的个数(不包括'\0')

struct s
{
	char name[20];
    char sex[3];
	int age;
};

int main()
{
	struct s s1 = {"xiaozhang","nv",20};
	struct s s2 = { 0 };
	char buf[100] = { 0 };
	//把s1中格式化数据转化成字符串放到buf中
	sprintf(buf, "%s %s %d", s1.name, s1.sex, s1.age);  
	printf("%s\n", buf);//buf的内容为“xiaozhang nv 20”
	
	//从字符串buf中获取一个格式化的数据放到s2中
	sscanf(buf, "%s %s %d", s2.name, s2.sex, &(s2.age));
	printf("格式化:%s %s %d\n", s2.name, s2.sex, s2.age);
	return 0;
}

注意:格式化数据如果有字符串,字符串不能有空格,否则就会被拆开,比如“xiaozhang”写成“xiao zhang”计入buf中,再一次被转化为格式化数据放在s2中,会变成“xiao”和“zhang”两个字符串。

比较一下三对函数:

scanf 是针对标准输入流的格式化输入语句
printf 是针对标准输出流的格式化输出语句
fscanf 是针对所有输入流的格式化输入语句
fprintf 是针对所有输入流的个时候输出语句
sscanf 是将一个字符串转化成格式化的数据的语句
sprintf 是将格式化的数据转化成字符串的语句

3.4 fread 和 fwrite

struct s
{
	char name[20];
    char sex[3];
	int age;
};

int main()
{
	struct s s1 = { "zhangsan","nv",20 };
	//以二进制的形式写到文件中
	FILE * pf = fopen("test.txt", "wb");
	if (pf == NULL)
	{
		perror("fopen:");
		return;
	}
	//以二进制的方式写
	fwrite(&s1, sizeof(struct s), 1, pf);//把s1的内容以二进制的方式写进文件中,
    //每次写1个struct s字节大小,如果成功写入,则返回1
	fclose(pf);
	pf = NULL;
	return 0;
}
    //以二进制的方式读
	fread(&s1, sizeof(struct s), 1, pf);//把s1的内容以二进制的方式写进文件中,每次写1个struct s字节大小,如果成功写入,则返回1
	fclose(pf);
	pf = NULL;
	printf("%s %s %d", s1.name, s1.sex, s1.age);
	return 0;

4.文件的随机读写

之前都是文件的顺序读写,如果我们要直接能够定位文件中的内容,怎么办?此时我们应该学习如何随机读写文件,接下来,我会向大家介绍fseek,ftell,rewind 函数来实行文件的随机读写。

我们可以在Cplusplus中搜到这三个函数的原型:

int fseek ( FILE * stream, long int offset, int origin );
long int ftell ( FILE * stream );
void rewind ( FILE * stream );
//SEEK_SET   起始位置
//SEEK_CUR  现在位置
//SEEK_END  文件末尾
//文件内容为:abcdefg
int main()
{
	FILE *pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		ferror("fopen");
		return;
	}
	fseek(pf, 2, SEEK_CUR);//找到文件中,文件开头往后2个字节处的位置
	int ch = fgetc(pf);//c
	printf("%c\n", ch);
	printf("%d\n", ftell(pf));//3
	fseek(pf, -1, SEEK_END); //找到文件中,距离文件末尾往前1个字节处的位置
	ch = fgetc(pf);//f
	printf("%d\n", ch);
	printf("%c\n", ftell(pf));//6

	rewind(pf);//让文件指针指向文件初始位置
	ch = fgetc(pf);//a
	printf("%c\n", ch);
	fclose(pf);
	pf = NULL;
}

总结一下三个函数的作用: 

  • fseek 根据文件指针的位置和偏移量来定位文件指针
  • ftell 返回文件指针相较于起始位置的偏移量
  • rewind 让指针回到文件的起始位置

正文内容结束啦,觉得我的博客有用的话就帮我点个赞吧! 

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