c语言自学教程——文件操作(下篇)

122 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第22天,点击查看活动详情

目前在不断更新《c语言自学教程》
未来会讲到《数据结构算法》,《C++语言》,《Linux系统编程》,《Linux网络编程》,《MySQL数据库》等。
期待系统学习编程的小伙伴可以关注我,不错过!

1. 文件的顺序读写

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

综合运用

//从data.txt拷贝一份放到data2.txt
int main()
{
	FILE *pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		printf("open for reading:%s\n", strerror(errno));
		return 0;
	}
	FILE *pw = fopen("data2.txt", "w");
	if (pw == NULL)
	{
		printf("open for wrriting:%s\n", strerror(errno));
		fclose(pf);
		return 0;
	}

	int ch = 0;
	while ((ch = fgetc(pf)) != EOF)
	{
		fputc(ch, pw);
	}
	fclose(pf);
	fclose(pw);
	pf = NULL;
	pw = NULL;
	return 0;
}

二进制输入输出函数fwrite,fread在[通讯录小项目]的改进中有用到,感兴趣的可以去看看.

1.1 对比一组函数:

scanf/fscanf/sscanf printf/fprintf/sprintf

详情见博文[scanf/fscanf/sscanf,printf/fprintf/sprintf函数的对比]

2. 文件的随机读写

2.1 fseek

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

int fseek ( FILE * stream, long int offset, int origin );

2.2 ftell

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

long int ftell ( FILE * stream );

2.3 rewind

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

void rewind ( FILE * stream );

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

根据数据的组织形式,数据文件被称为文本文件或者二进制文件。 数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件。 如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件。

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

字符一律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。 如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节),而二进制形式输出,则在磁盘上只占4个字节(VS2013测试)

在这里插入图片描述 测试代码:

#include <stdio.h>
int main()
{
	int a = 10000;
	FILE* pf = fopen("test.txt", "wb");
	fwrite(&a, 4, 1, pf);//二进制的形式写到文件中
	fclose(pf);
	pf = NULL;
	return 0;
}

在这里插入图片描述 在这里插入图片描述 是因为10000可以表示为:请添加图片描述

4. 文件读取结束的判定

4.1 常被错误使用的feof

牢记:在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束。 而是应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束。 1.文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets ) 例如:

  • fgetc 判断是否为 EOF .
  • fgets 判断返回值是否为 NULL .

2.二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。 例如:

  • fread判断返回值是否小于实际要读的个数。

二进制文件的例子:

#include <stdio.h>
enum { SIZE = 5 };
int main(void)
{
	double a[SIZE] = {1.,2.,3.,4.,5.};
	FILE *fp = fopen("test.bin", "wb"); // 必须用二进制模式
	fwrite(a, sizeof *a, SIZE, fp); // 写 double 的数组
	fclose(fp);
	
	double b[SIZE];
	fp = fopen("test.bin","rb");
	size_t ret_code = fread(b, sizeof *b, SIZE, fp); // 读 double 的数组
	if(ret_code == SIZE) {
		puts("Array read successfully, contents: ");
		for(int n = 0; n < SIZE; ++n) printf("%f ", b[n]);
		putchar('\n');
	} else { // error handling
	if (feof(fp))
		printf("Error reading test.bin: unexpected end of file\n");
	else if (ferror(fp)) {
		perror("Error reading test.bin");
		}
	}
	fclose(fp);
}

5. 文件缓冲区

ANSIC 标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。 从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。 如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根据C编译系统决定的。 在这里插入图片描述

#include <stdio.h>
#include <windows.h>
//VS2013 WIN10环境测试
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在关闭文件的时候,也会刷新缓冲区
	pf = NULL;
	
	return 0;
}

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

文件操作在c语言中是一个较为独立的版块,但也希望大家能够有所收获。