C语言中的文件和文件操作

目录

  • 为什么使用文件
  • 什么是文件
  • 二进制文件和文本文件?
  • 文件的打开和关闭
  • 文件的顺序读写
  • 文件的随机读写
  • 文件读取结束的判定
  • 文件缓冲区

1、为什么使用文件
如果没有文件,我们写的程序是不能被长久保存的,因为程序的数据存储在电脑的内存中,程序退出,内存就会被回收,数据就会丢失,因此想要将数据长久的保存是少不了文件的
2、什么是文件
磁盘上的文件是文件,但是在程序设计中,我们⼀般谈的⽂件有两种:程序文件、数据文件(从文件功能的⻆度来分类的)
2.1、程序文件
程序文件包括源文件(后缀为.c)目标文件(Windows环境后缀为.obj),可执行程序(Windows环境下后缀为.exe)
2.2、
数据文件
文件的內容不一定是程序,而是程序运行时读写的数据
3、二进制文件和文本文件
二进制文件就是以二进制的形式存储的文件,文本文件就是以ASCII码值的形式存储的文件

代码测试

//如何存储和打开文件呢
int main()
{
	FILE* pf = fopen("text.txt", "wb");//wb--以二进制的形式写文件
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	int a = 15;
	fwrite(&a, 4, 1, pf);//将1个4个字节大小的a写入到pf指向的文件中
	fclose(pf);
	pf = NULL;
	return 0;
}

下图表示运行成功
在这里插入图片描述
右击源文件添加现有项
在这里插入图片描述
右击text.txt选择打开方式为二进制编译器即可观察存储是否正确
在这里插入图片描述
4、文件的打开和关闭
4.1、流和标准流
简单来说流就是运送字符的一个通道
标准流
stdin–标准输入流,在大多数环境中从键盘输入,例如scanf函数
stdout–标准输出流,在大多数环境中输出到显示器界面,例如printf函数
stderr–标准错误流,大多数环境中输出到显示器界面
以上三个流在程序启动时,是默认打开的状态,他们的类型是FILE*,通常称为文件指针,C语言中就是通过FILE*的文件指针来维护流的各种操作
4.2、文件指针
每个被使用的文件都会在内存中开辟一个相应的文件信息区,用来存放文件的相关信息,这些信息存放在一个结构体变量中,该结构体由系统声明,取名FILE

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

以上是VS2013编译环境提供的文件类型声明,不同环境下的文件类型声明可能不一样但是大同小异,使用者一般通过一个文件指针来维护所开辟的文件信息区,这样使用起来更方便,通过文件指针找到与其关联的文件
4.3、文件的打开和关闭
文件在读写之前应该先打开文件,使用结束之后要关闭文件,在编写程序的时候,在打开文件的同时都会返回一个FILE*的指针变量指向该文件,相当于建立了指针和文件的联系,使用fopen函数来打开文件,fclose函数来关闭文件

FILE* fopen(const char* filename,const char* mode);
int fclose(FILE* stream);

filename–表示文件的名字
mode–表示文件的打开方式
常见的文件打开方式
“r”–只读,打开一个已经存在的文件,读取里面的数据,如果文件不存在就会报错
“w”–只写,如果文件存在,清空文件的原数据并重新写入数据,文件如果不存在则创建该文件并写入数据
“a”–追加,如果文件存在,在该文件的原数据的基础上继续写入数据,不存在就创建并写入
“rb”–二进制的只读,本质上和"r"差不多就是读的形式是二进制
“wb”–二进制的只写,本质上和"w"差不多就是写的形式是二进制
“ab”–二进制的追加

5、文件的顺序读写

5.1、顺序读写的函数介绍
在这里插入图片描述
5.2、顺序读写函数的使用
int fgetc(FILE* stream) 一次读取一个字符

int main()
{
	FILE* pf = fopen("text.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	char ch = fgetc(pf);
	printf("%c", ch);
	fclose(pf);
	pf = NULL;
	return 0;
}

int fptuc(int _Charter,FILE* stream) 一次写入一个字符

int main()
{
	FILE* pf = fopen("text.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	char ch = 'a';
	for (ch = 'a'; ch <= 'z'; ch++)
	{
		fputc(ch, pf);
	};
	fclose(pf);
	pf = NULL; 
	return 0;
}

int fputs(const char* Buffer,FILE* stream)一次写入一行数据
Buffer–表示字符数组的首元素地址

int main()
{
	FILE* pf = fopen("fputs.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fputs("hello world\n", pf);
	fputs("cctv", pf);
	fclose(pf);
	pf = NULL;
	return 0;
}

写入效果在这里插入图片描述
char * fgets ( char * str, int num, FILE * stream ) 一次读取一行数据
str–存储数组的起始地址,最多存num-1个元素,还有一个位置放\0,stream–文件流

int main()
{
	FILE* pf = fopen("fputs.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	char ch[20];
	fgets(ch, 20, pf);
	printf("%s", ch);
	fclose(pf);
	pf = NULL;
	return 0;
}

int fscanf ( FILE * stream, const char * format, … ) 读取字符串中格式化的数据

struct stu
{
	char name[10];
	int age;
	char address[20];
};
int main()
{
	FILE* pf = fopen("fprintf.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	struct stu s= { 0 };
	fscanf(pf, "%s %d %s", &s.name, &s.age, &s.address);
	//将pf指向的文件中的格式化的数据读取到结构体s中
	printf("%s %d %s", s.name, s.age, s.address);
	fclose(pf);
	pf = NULL;
	return 0;
}

int fprintf ( FILE * stream, const char * format, … ) 将字符串中的格式化的数据写入文件指针指向的文件中

struct stu
{
	char name[10];
	int age;
	char address[20];
};
int main()
{
	FILE* pf = fopen("fprintf.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	struct stu s = { "zhangsan",12,"wuhan" };
	fprintf(pf, "%s %d %s", s.name, s.age, s.address);//将格式化的数据写入pf指向的文件中
	fclose(pf);
	pf = NULL;
	return 0;
}

size_t fread ( void * ptr, size_t size, size_t count, FILE * stream )
从文件中读取大小为size,个数为count个的元素到ptr数组中

int main()
{
	FILE* pf = fopen("text.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	char read[30];
	fread(read, sizeof(char), 5, pf);
	for (int i = 0; i < 5; i++)
	{
		printf("%c", read[i]);
	}
	fclose(pf);
	pf = NULL;
	return 0;
}

size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream )

int main()
{
	FILE* pf = fopen("fwrite.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	char write[] = "abcdefg";
	fwrite(write, sizeof(char), 3, pf);
	//将write数组中的前三个元素写入到pf指向的文件中
	fclose(pf);
	pf = NULL;
	return 0;
}

5.3、对比scanf/fscanf/sscanf和printf/fprintf/sprintf
scanf是针对标准输入流stdin的格式化输入流函数
printf是针对标准输出流stdout的格式化输出流函数
fscanf针对所有输入流的格式化输入函数
fprintf针对所有输出流的格式化输出函数
sscanf将字符串中的格式化数据读取出来
sprintf将格式化的数据写入到字符数组中

//sprintf
int main_sprintf()
{
	struct stu s = { "zhangsan",20,"wuhan" };
	char string[50];
	sprintf(string, "%s %d %s", s.name, s.age, s.address);
	//将格式化的数据写入到字符数组string中
	printf(string);
	return 0;
}
int main_sscanf()
{
	char arr[] = "I am 10 years old this year";
	char str[30];
	int i;
	sscanf(arr, "%s %*s %d", str, &i);
	//%*s -- 表示读取一个字符串,但不对其进行赋值,忽略这个读取结果,所以才能使得%d读取到10对i进行赋值
	printf("%s %d", str, i);
	return 0;
}

6、文件的随机读写

6.1、fseek
int fseek ( FILE * stream, long int offset, int origin )
offset–表示偏移量
origin–有三个取值SEEK_SET(文件的起始位置) 、SEEK_CUR (文件的当前位置)、SEEK_END(文件的末尾)

int main()
{
	FILE* pf = fopen("text.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	char ch;
	ch=fgetc(pf);
	printf("%c\n", ch);
    fseek(pf,2, SEEK_SET);//相对于文件起始位置偏移2
	ch = fgetc(pf);//获取定位后的文件指针指向的字符
	printf("%c\n", ch);
	fclose(pf);
	pf = NULL;
	return 0;
}

6.2、ftell
long int ftell ( FILE * stream )
ftell – 返回文件指针相对于起始位置的偏移量

int main()
{
	FILE* pf = fopen("text.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	char ch;
	ch = fgetc(pf);//第一次读取
	printf("%c\n", ch);
	ch = fgetc(pf);//第二次读取
	printf("%c\n", ch);
	int a=ftell(pf);//此时文件指针相对于起始位置偏移量为2
	printf("%d\n", a);
	fclose(pf);
	pf = NULL;
	return 0;
}

6.3、rewind
void rewind ( FILE * stream ) 让文件指针返回到起始位置

int main()
{
	FILE* pf = fopen("text.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	char ch;
	ch = fgetc(pf);//第一次读取
	ch = fgetc(pf);//第二次读取
	rewind(pf);//让文件指针的位置返回到文件的起始位置
	ch = fgetc(pf);//再次读取,读取到的就是第一次读取的字符
	printf("%c\n", ch);
	fclose(pf);
	pf = NULL;
	return 0;
}

7、文件读取结束的判定
feof—它是用来判断文件读取结束的原因是否是遇到了文件的末尾而结束
ferror—它是用来判断文件读取结束的原因是否是读取失败而结束

文本文件读取结束判断返回值是否为EOF或NULL
例如:
fgetc 读取失败或者是遇到文件的末尾都会返回EOF
fgets 读取失败或者是遇到文件的末尾会返回NULL

二进制文件读取结束判断返回值是否小于要读取的个数
例如:fread

代码案例展示:
文本文件读取结束判定读取结束的原因

int main()
{
	FILE* pf = fopen("text.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	int i=fgetc(pf);
	while ((i=fgetc(pf))!=EOF)
	{
		;
	}
	if (feof(pf))
	{
		printf("EOF");
	}
	else if (ferror(pf))
	{
		printf("NULL");
	}
	 fclose(pf);
	 pf=NULL;
	 return 0;
}

二进制文件读取结束的判定

int main()
{
	FILE* pf = fopen("text.txt", "rb");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	char read[20];
	int size=fread(read, sizeof(char), 3, pf);
	if (size == 3)
	{
		printf("读取成功\n");
	}
	else
	{
		if (feof(pf))
			printf("EOF\n");
		else if (ferror(pf))
			printf("NULL");
	}
	fclose(pf);
	pf = NULL;
	return 0;
}

8、文件缓冲区

#include <Windows.h>
int main()
{
	FILE* pf = fopen("huanchongqu.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fputc('c', pf);
	Sleep(10000);//让程序运行到下一步的速度迟缓10秒
	//休眠时间打开文件,会发现里面还没有数据
	fflush(pf);//刷新缓冲区,让缓冲区的数据写到文件中
	Sleep(10000);
	//此时打开文件,数据就又有了
	fclose(pf);//在关闭文件时也会刷新缓冲区
	pf = NULL;
	return 0;
}

因为有了缓冲区的存在,在对文件读写数据时要刷新缓冲区或者在文件结束的时候关闭文件,否则会造成读写文件的问题

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值