C语言——必须掌握的文件相关操作


前言


一、文件相关的操作

什么是文件

但是在程序设计中,我们一般谈的文件有两种:程序文件、数据文件
程序文件

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

数据文件

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

本章讨论的是数据文件。
我们以前只把数据输出到显示器,从键盘读取数据。
显示器是标准输出设备(stdout),键盘是标准输入设备(stdin)
而我们学习文件可类比于对终端的操作,站在内存的视角输出和读入。

文件名
一个文件要有一个唯一的文件标识,以便用户识别和引用。
文件名包含3部分:文件路径+文件名主干+文件后缀
例如: c:\code\test.txt
为了方便起见,文件标识常被称为文件名。

文本文件和二进制文件

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

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

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

缓冲区

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

  1. fflush(stdin)
    fflush(stdin)是C中的一个函数,用来刷新缓冲区,如果刷新成功返回的是 0,指定的流没有缓冲区或者只读打开时也返回0值。返回EOF指出一个错误。
  2. getchar函数读取
	while ((ch = getchar()) != EOF && ch != '\n')
	{
		;//读取处于缓冲区的字符
	}	

文件指针

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

所以文件相关的操作是基于文件结构体来创建的。
定义文件指针

FILE* pf;

文件的打开和关闭

文件打开函数fopen()
在这里插入图片描述

int main()
{
	FILE* pf = fopen("data.txt", "r");//打开文件data.txt以只读的方式
	//判断是否打开成功
	if (pf == NULL)
	{
		printf("打开文件失败\n");
		exit(-1);
	}
	return 0;
}

返回类型是一个文件指针,然后就可以对文件进行相关操作。
文件既然打开就要关闭
关闭函数fclose
在这里插入图片描述

fclose(pf);//类似free

文件打开方式
在这里插入图片描述

二、文件读写

在这里插入图片描述

文件的顺序读写

fputc

  • 函数原型:int fputc( int c, FILE *stream );

  • 函数作用:向文件写入一个字符

  • 函数参数:字符,文件指针

  • 函数返回值:写入成功返回该字符,失败返回EOF(-1)

  • 函数用法:

fputc('a',pf);//把字符a写入文件

//测试返回值
int ch=fputc('a',pf);//把字符a写入文件
printf("%c\n", ch);//a

fgetc

  • 函数原型:int fgetc( FILE *stream );

  • 函数作用:从文件读取一个字符

  • 函数参数:文件指针

  • 函数返回值:返回字符的ASCII码,如果读取失败或者遇到文件末尾返回EOF

  • 函数用法:

int main()
{
	FILE* pf = fopen("data.txt", "w+");
	if (NULL == pf)
	{
		printf("打开文件失败\n");
		exit(-1);
	}
	//写文件
	fputc('a',pf);//把字符a写入文件
	fputc('b',pf);//把字符c写入文件
	fputc('c',pf);//把字符c写入文件

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

	ch = fgetc(pf);
	printf("%c\n", ch);//b

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

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

fputs

  • 函数原型:int fputs( const char *string, FILE *stream );

  • 函数作用:向文件写入一段字符串

  • 函数参数:字符指针,文件指针

  • 函数返回值:写入成功返回非负值,写入失败返回EOF

  • 函数用法:

#include <stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		printf("打开文件失败\n");
		return -1;//文件打开失败,失败返回
	}
	//对文件进行输入字符串操作
	char arr[10] = "abcdef";
	fputs(arr, pf);

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

fgets

  • 函数原型:char *fgets( char *string, int n, FILE *stream );

  • 函数作用:读取文件的指定数量n个字符到string指向的字符串中

  • 函数参数:准备写入的字符串地址,读取的字符个数,文件指针

  • 函数返回值:如果读取成功返回string,读取失败返回NULL

  1. 在fgets函数读取到指定字符数之前,若读取到换行符(’\n’),则停止读取,读取带回的字符包含换行符。
  2. 直到fgets函数读取到第n-1个字符时都没有遇到换行符(’\n’),则返回读取到的n-1个字符,并在末尾加上一个空字符(’\0’)一同返回(共n个字符)。
  • 函数用法:
#include <stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		printf("打开文件失败\n");
		return -1;//文件打开失败,失败返回
	}
	
	char arr[10] = { 0 };
	//读取文件的指定数量5个字符到arr指向的字符串中
	//实际只读到4个有效字符(假设文件字符数大于4),最后还要加一个'\0'
	char* temp = fgets(arr, 5, pf);
	if (temp == NULL)
	{
		printf("读取失败\n");
		exit(-1);
	}
	printf("%s\n", arr);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

fprintf

  • 函数原型:int fprintf( FILE *stream, const char *format [, argument ]…);
    类比printf:int printf( const char *format [, argument]… );
    在前面加一个文件指针即可

  • 函数作用:向文件格式化写入数据

  • 函数参数:文件指针,和相关格式符及对应的变量

  • 函数返回值:返回成功写入的字节数

  • 函数用法:

#include <stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		printf("打开文件失败\n");
		return -1;//文件打开失败,失败返回
	}
	//对文件进行格式化写入操作
	struct st
	{
		int i;
		char arr[10];
	};
	struct st s1 = { 1,"zhangsan" };
	fprintf(pf,"%d %s", s1.i, s1.arr);

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

fscanf

  • 函数原型:int fscanf( FILE *stream, const char *format [, argument ]… );

类别scanf的使用:int scanf( const char *format [,argument]… );
只多了一个文件指针而已。

  • 函数作用:格式化读取文件内容

  • 函数参数:文件指针,和相关格式符及要存储的变量地址

  • 函数返回值:读取成功返回读取的字段数,失败返回EOF

  • 函数用法:

#include <stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		printf("打开文件失败\n");
		return -1;//文件打开失败,失败返回
	}
	//对文件进行格式化读取操作
	struct st
	{
		int i;
		char arr[10];
	};
	struct st s1 ;
	fscanf(pf,"%d %s", &(s1.i), s1.arr);
	printf("%d %s", s1.i, s1.arr);

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

fwrite

  • 函数原型:size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );

  • 函数作用:将buffer指针指向的大小位size个字节的内容写count个到文件里

  • 函数参数:无类型指针,单个数据大小,写入的个数,文件指针

  • 函数返回值:返回实际写入的完整项的数量,小于这个数量则失败,发生错误则无法继续正常使用文件指针(无法确定其位置)

  • 函数用法:

#include <stdio.h>
int main()
{
	//打开文件-- wb
	FILE* pf = fopen("data.txt", "wb");
	if (pf == NULL)
	{
		printf("打开文件失败\n");
		return -1;//文件打开失败,失败返回
	}
	//对文件进行二进制写入操作
	struct st
	{
		int i;
		char arr[10];
	};
	struct st s1 = {2,"zhangsan"};
	fwrite(&s1, sizeof(s1), 1, pf);//写一个结构体的内容


	//printf("%d %s", s1.i, s1.arr);

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

fread

  • 函数原型:size_t fread( void *buffer, size_t size, size_t count, FILE *stream );

  • 函数作用:将文件里大小的size个字节的内容读count个到buffer指针指向的空间

  • 函数参数:无类型指针,单个数据大小,读入的个数,文件指针

  • 函数返回值:返回实际读取的完整项的数量,小于这个数量则失败

  • 函数用法:

#include <stdio.h>
int main()
{
	//打开文件-- rb
	FILE* pf = fopen("data.txt", "rb");
	if (pf == NULL)
	{
		printf("打开文件失败\n");
		return -1;//文件打开失败,失败返回
	}
	//对文件进行二进制写入操作
	struct st
	{
		int i;
		char arr[10];
	};
	struct st s1 = { 0 };
	
	fread(&s1, sizeof(s1), 1, pf);
	printf("%d %s", s1.i, s1.arr);

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

文件的随机读写

fseek

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

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

三个位置 对应参数origin
在这里插入图片描述
offset是相对origin的偏移量

ftell

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

long int ftell ( FILE * stream );

rewind

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

void rewind ( FILE * stream );

用法

int main()
{
	//1. 打开文件
	FILE* pf = fopen("data.txt", "r");
	if (NULL == pf)
	{
		perror("fopen");
		return -1;
	}
	//假设文件里面内容是abcd
	//2. 读文件
	//随机读写
	//int ch = fgetc(pf);//
	//printf("%c\n", ch);

	//如果第一次就要读取'c'
	fseek(pf, 2, SEEK_SET);//起始位置a偏移2
	int ch = fgetc(pf);//c
	printf("%c\n", ch);

	fseek(pf, -2, SEEK_CUR);
	ch = fgetc(pf);//b
	printf("%c\n", ch);

	//计算文件指针相对于起始位置的偏移量
	int ret = ftell(pf);
	printf("%d\n", ret);


	rewind(pf);//回到文件的起始位置
	ch = fgetc(pf);//a
	printf("%c\n", ch);

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

	return 0;
}

三、文件的结束判定

feof函数

int feof( FILE *stream );

feof函数的功能也是判断使用某一文件指针的过程中,是否读取到文件末尾,若使用时没有读取到文件末尾,则feof函数返回0;否则,feof函数将返回一个非零的值。调用feof函数时,也只需将待检查的文件指针传入即可。

被错误使用的 feof

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

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

ferror函数

int ferror( FILE *stream );

ferror函数的功能就是判断使用某一文件指针的过程中,是否发生错误,若使用时没有发生错误,则ferror函数返回0;否则,ferror函数将返回一个非零的值。调用ferror函数时,我们只需将待检查的文件指针传入即可。

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

总结

文件相关操作可能在以后的学习中不会使用到,但能很好的锻炼我们学习能力,类比的进行学习。
希望能够帮助到你!

  • 37
    点赞
  • 163
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

s_persist

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值