【C语言】文件操作

目录

1.什么是文件

2.二进制文件和文本文件

3.文件的打开和关闭

4.文件的顺序读写

 4.1 fputc 和 fgetc

4.2 fputs 和 fgets

4.3 fprintf 和 fscanf

4.4 fwrite 和 fread


1.什么是文件

  我们写的程序储存在电脑中,如果程序退出,内存被回收数据就丢失了,再次运行时就看不到上次运行的数据,如果要将数据进行持久化的保存,就要使用文件。

  在程序设计中,我们所讨论的文件从功能的角度划分为两种:程序文件、数据文件

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

数据文件:文件内容不一定是程序,而是程序运行时读写的数据

  文件名:一个文件要有一个唯一的文件路径,以便用户识别和引用,包括3部分:文件路径+文件名+文件后缀

2.二进制文件和文本文件

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

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

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

字符一律以ASCII码形式存储,数值型数据既可以用ASCII码存储,也可以使用二进制的形式存储,例如10000,ASCII码形式输出要占5个字节,因为有5个字符,二进制形式输出,在磁盘上占4个字节

 3.文件的打开和关闭

文件在读写之前应该先打开文件,在使用结束结束后关闭文件,在编写程序的时候,打开文件的同时,都会返回一个FILE*的指针变量指向该文件。

我们用fopen函数打开文件,fclose来关闭文件。

//打开文件
FILE* fopen( const char* filename, const char* mode );

//关闭文件
int fclose( FILE* stream );

fopen函数的第一个参数是 要打开的文件名,第二个参数是 打开方式,下面是文件的打开方式

比如我们现在写一个代码

#include <stdio.h>
int main()
{
	FILE* pf = fopen("test.txt", "r");//以读的形式打开
	if (pf == NULL) //判断是否打开成功
	{
		perror("fopen");
		return 1;
	}

	fclose(pf);//关闭文件
	pf = NULL;//置空
	return 0;
}

 fclose关闭文件的时候,pf没有指向的东西,此时pf为野指针,所以最好把pf置为NULL

此时"test.txt"文件不存在这个工程里面

当代码运行起来时pf就会 返回NULL,结果报出错误,fopen: No such file or directoryfopen: No such file or directory  ,没有这个文件或者文件夹 

 当我们新建一个"test.txt"的文件之后再运行,程序不会报错,正常运行

 当我们用"w"打开时,原本文件中如果有内容,会全部清空,如下,代码为

#include <stdio.h>
int main()
{
	FILE* pf = fopen("test.txt", "w");//以写的形式打开
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	fclose(pf);//关闭文件
	pf = NULL;//置空
	return 0;
}

运行前的文件

 运行时的代码

 运行后的文件,内容已被清空

现在,我们再把"test.txt"这个文件删除

以"w"的形式运行,和刚刚代码一样,看看结果

运行不会出错,而且自动创建了"test.txt"这个文件

 别的形式步骤也是一样的,得到的结果会不一样,根据上面的表自己研究研究,这里就不一个一个示范了

4.文件的顺序读写

顺序读写相关函数表如下

 4.1 fputc 和 fgetc

我们先看fputc,这个函数的功能就是写字符到文件对应的流中去

 返回值是,如果成功返回你输入的字符,如果失败,会把这个错误标记起来

 比如我们写个'a' 'b' 'c'到文件里面去

#include <stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "w");//我们要写数据进去
	if (pf == NULL) //判断
	{
		perror("fopen");
		return 1;
	}

	//写文件
	fputc('a', pf);
	fputc('b', pf);
	fputc('c', pf);


	fclose(pf);//关闭文件
	pf = NULL;//置空
	return 0;
}

此时没有发生任何错误,成功写入,这里,每一次调用这个函数,文件的光标就会自动后移

 当然不止这一种写法,fputc一次写入一个字符,所以也可以用循环进行写入

fgetc

参数就一个文件指针,就是从这个文件里面读数据

 读取成功,会返回这个字符的ASCII码值,读取失败,返回EOF

 比如我们还是从"test.txt"这个文件中读,此代码只写读文件的部分,打开文件和关闭文件跟上面是一样的,替换一下就好了

//读文件
int ch = fgetc(pf);
printf("%c", ch);

ch = fgetc(pf);
printf("%c", ch);

ch = fgetc(pf);
printf("%c", ch);

 读文件,然后打印出来

如果想读文件中所有内容,可以用循环

//读文件
int ch = 0;
while ((ch = fgetc(pf))!=EOF)
{
	printf("%c", ch);
}

4.2 fputs 和 fgets

如果想输入或输出一串数据,就可以用fputs 和 fgets

把str指向的字符串写到stream里去,遇到'\0'停止,比如下面这个代码

#include <stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "w");//我们要写数据进去
	if (pf == NULL) //判断
	{
		perror("fopen");
		return 1;
	}
	//写文件
	fputs("hello", pf);

	fclose(pf);//关闭文件
	pf = NULL;//置空
	return 0;
}

代码正常运行

 

当我们再写一行时,是接着"hello"还是会换行呢,看下面的代码

#include <stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "w");//我们要写数据进去
	if (pf == NULL) //判断
	{
		perror("fopen");
		return 1;
	}
	//写文件
	fputs("hello", pf);
	fputs("world", pf);

	fclose(pf);//关闭文件
	pf = NULL;//置空
	return 0;
}

 运行起来看结果,写在了一行上

所以如果要换行,需要自己加换行符 

 这个函数有3个参数,意思是从stream里面读num个字节的数据到str指向的里面去,这里需要注意的是 要读num个字节的数据,其实读的个数是num-1,因为最后一个字节需要存放'\0'

看下面的代码

#include <stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");//我们要读数据进去
	if (pf == NULL) //判断
	{
		perror("fopen");
		return 1;
	}
	//读文件
	char arr[10] = { 0 };
	fgets(arr, 10, pf);

	fclose(pf);//关闭文件
	pf = NULL;//置空
	return 0;
}

 我们打开监视窗口观察一下

看最右边的监视窗口里面的arr数组,可以看到只把前9个字符读进去了,最后1个位置放了'\0'

假如我们第一行不够10个字符怎么办,我们来看下面的代码,现在文件里面只有"hello\n",为了方便观察,把arr数组里面的值改一下

可见,如果这行不够10个字符,连\n都读进去,再加上\0,这个函数就是只读一行,换行不读取,想换行需要再调用一次这个函数

如果读取成功,这个函数的返回值就是str,如果失败,遇到了文件末尾或错误,就会返回NULL,所以现在代码就可以写成下面这样

#include <stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");//我们要读数据进去
	if (pf == NULL) //判断
	{
		perror("fopen");
		return 1;
	}
	//读文件
	char arr[20] = { 0 };
	while (fgets(arr, 20, pf) != NULL)
	{
		printf("%s", arr);
	}

	fclose(pf);//关闭文件
	pf = NULL;//置空
	return 0;
}

运行看结果

 还有一种情况是一排的内容比20个还多呢?结果依然是可以完全读取的,自己改变文件的内容试一试

4.3 fprintf 和 fscanf

对比来看fprintf和printf

 可见,fprintf只是多了一个参数而已,其他与printf没区别,会用printf,就会fprintf,举个例子

#include <stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "w");//我们要写数据进去
	if (pf == NULL) //判断
	{
		perror("fopen");
		return 1;
	}
	//写文件
	char arr[20] = "zhengsan";
	//printf("%s", arr); //printf的用法
	fprintf(pf, "%s", arr);//fprintf的用法

	fclose(pf);//关闭文件
	pf = NULL;//置空
	return 0;
}

正常运行,写入成功,并且是以文本的形式写进去的

 如果要写多条数据,也是一样,循环起来

 

我们同样来对比一下fscanf和scanf

也是只有参数的区别,会用scanf就会fscanf,举个例子

#include <stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");//我们要读数据进去
	if (pf == NULL) //判断
	{
		perror("fopen");
		return 1;
	}
	//读文件
	char arr[20] = "zhengsan";
	//scanf("%s", arr); //scanf的用法
	fscanf(pf, "%s", arr);//fscanf的用法

	printf("%s", arr);//打印到屏幕上看看

	fclose(pf);//关闭文件
	pf = NULL;//置空
	return 0;
}

结果为

 

 

4.4 fwrite 和 fread

我们4.1 4.2 4.3 所说的函数可以适用于所有的输入输出流,也就是说可以在文件输入输出,也可以键盘输入或者屏幕上输出,且都是以文本的形式读写的,而fwrite和fread只能在文件输入输出,以二进制形式读写

 参数的意思是:ptr, 指向要被写的数组  ; size, 每写元素的长度,单位是字节 ;count, 一次要写的元素个数;最后一个参数就是文件流

从ptrr指向的数组里面写count个大小为size个字节的数据放到文件流里

举例如下

#include <stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "wb");//以wb,二进制的形式写
	if (pf == NULL) //判断
	{
		perror("fopen");
		return 1;
	}
	//写文件
	int arr[] = { 1,2,3,4,5 };
	int sz = sizeof(arr) / sizeof(arr[0]);//算数组大小
	fwrite(arr, sizeof(int), sz, pf);

	fclose(pf);//关闭文件
	pf = NULL;//置空
	return 0;
}

运行看结果

 

写入成功,但是我们看不懂,因为是以二进制的形式写的 

我们再以二进制的形式读

参数和fwrite类似: 从流里面读count个大小为size个字节的数据放到ptr指向的数组里,看下面的代码

#include <stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "rb");//以rb,二进制的形式读
	if (pf == NULL) //判断
	{
		perror("fopen");
		return 1;
	}
	//读文件
	int arr[5] = { 0 };
	fread(arr, sizeof(int), 5, pf);
	for (int i = 0; i < 5; i++)
	{
		printf("%d ", arr[i]);
	}

	fclose(pf);//关闭文件
	pf = NULL;//置空
	return 0;
}

运行后结果

 这个函数的返回值是读取的个数,如果我们不知道文件有多少个时,我们可以这样写

#include <stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "rb");//以rb,二进制的形式读
	if (pf == NULL) //判断
	{
		perror("fopen");
		return 1;
	}
	//读文件
	int arr[5] = { 0 };
	int i = 0;
	while (fread(arr+i, sizeof(int), 1, pf))//一次读一个
	{
		printf("%d ", arr[i]);
		i++;
	}

	fclose(pf);//关闭文件
	pf = NULL;//置空
	return 0;
}

当返回0时退出循环

5.文件的随机读写

5.1 fseek

根据文件指针的位置和偏移量来定位文件指针(也就是文件光标)

第一个参数就是文件指针,第二个参数是偏移量,第三个参数就是从哪个位置偏移

 第三个参数有三个选择,如下

SEEK_SET是文件的起始位置;SEEK_CUR是文件指针当前位置;SEEK_END是文件末尾 

举个例子,

#include <stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");//读的形式打开
	if (pf == NULL) //判断
	{
		perror("fopen");
		return 1;
	}
	//读文件
	int ch = 0;
	ch = fgetc(pf);//a, 且光标向后移
	printf("%c\n", ch);//打印a

	fseek(pf, 5, SEEK_CUR);//此时应该指向g

	ch = fgetc(pf);//g,且光标向后移
	printf("%c\n", ch);//打印g

	fclose(pf);//关闭文件
	pf = NULL;//置空
	return 0;
}

当然,在此情况下,想要文件指针指向g还有别的办法,如

//fseek(pf, 6, SEEK_SET);
//fseek(pf, -2, SEEK_END);
//用SEEK_END的时候注意偏移量为负的

 给的起始位置不一样,偏移量就不一样

5.2 ftell

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

直接看例子 


#include <stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");//读的形式打开
	if (pf == NULL) //判断
	{
		perror("fopen");
		return 1;
	}
	//读文件
	int ch = 0;
	ch = fgetc(pf);//a, 且光标向后移
	printf("%c\n", ch);//打印a
	fseek(pf, 5, SEEK_CUR);//此时应该指向g,偏移量应该为6
	printf("%d\n", ftell(pf));//打印验证

	fclose(pf);//关闭文件
	pf = NULL;//置空
	return 0;
}

结果为6

 当我们代码再稍作修改,就可以得到文件长度

#include <stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");//读的形式打开
	if (pf == NULL) //判断
	{
		perror("fopen");
		return 1;
	}
	//读文件
	int ch = 0;
	ch = fgetc(pf);
	printf("%c\n", ch);
	fseek(pf, 0, SEEK_END);
	printf("%d\n", ftell(pf));

	fclose(pf);//关闭文件
	pf = NULL;//置空
	return 0;
}

 这个函数就是这样运用的

5.3 rewind

功能就是:让文件指针回到起始位置

这次分享就到这里,感谢观看!

  • 39
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值