一篇文章彻底捋顺C语言文件操作知识点

1.为什么使用文件

我们写的程序数据存储在电脑的内存里,如果没有文件,那么随着电脑关机内存释放,数据也就一去不复返了,所以需要文件来持久化的保存数据。

2.什么是文件

磁盘/硬盘上的文件叫文件,按功能分两种,程序文件和数据文件

2.1程序文件

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

2.2数据文件

文件的内容不一定是程序,而是程序运行时读取的数据。
我们有时候会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存使用,这里处理的就是磁盘上文件
在这里插入图片描述

2.3文件名

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

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

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

⼀个数据在⽂件中是怎么存储的呢?

字符⼀律以ASCII形式存储,数值型数据既可以⽤ASCII形式存储,也可以使⽤⼆进制形式存储

如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占⽤5个字节(每个字符⼀个字节),⽽⼆进制形式输出,则在磁盘上只占4个字节。
在这里插入图片描述
代码展示:

#include <stdio.h>
int main()
{
	int a = 10000000;
	FILE* pf=fopen("test.txt", "wb");
	fwrite(&a, 4, 1, pf);
	fclose(pf);
	pf = NULL;
	return 0;
}

vs打开二进制文件步骤
在这里插入图片描述
在这里插入图片描述
10000000在二进制文件中

在这里插入图片描述
这类就叫文本文件,直接就能看懂

4.文件的打开和关闭

4.1流和标准流

4.1.1 流

我们程序的数据需要输出到各种外部设备,也需要从外界获取数据,不同外设输入输出操作不同,为了方便程序员对外设操作,我们抽象出流的概念,我们可以把流想象成流淌着字符的河。

C程序针对文件,画面,键盘的数据输入输出都是通过流操作完成的
一般情况下,我们要想向流里写数据,或者从流中读取数据,都是要打开流,然后操作
在这里插入图片描述

4.1.2标准流

为什么我们从键盘输入数据,向屏幕输出数据,并没有打开流呢
那是因为C语言在启动时,默认打开了三个流

  • stdin-标准输入流,在大多数的环境中从键盘输入,scanf函数就是从标准输入流中读取数据
  • stdout- 标准输出流,在大多数环境中输出至显示屏,printf函数就是把数据输出到标准输出流
  • stderr-标准错误流,在大多数环境中输出至显示屏界面。

这是默认打开了这三个流,我们使用scanf和printf就是可以直接进行输入输出操作

stdin,stdout,stderr 三个流类型是FILE *,通常称为文件指针
C语言就是通过FILE *的文件指针来维护流的各种操作的

4.2文件指针

前⾯的内容中提到了FILE* 的指针类型,我们称为“⽂件类型指针”,简称“⽂件指针”。每个被
使⽤的⽂件都在内存中开辟了⼀个相应的⽂件信息区,⽤来存放⽂件的相关信息(如⽂件的名字,⽂
件状态及⽂件当前的位置等)。这些信息是保存在⼀个结构体变量中的。该结构体类型是由系统声明
的,取名FILE

stdio头⽂件中有以下的⽂件类型申明

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

不同的C编译器的FILE类型包含的内容不完全相同,但是⼤同⼩异。
每当打开⼀个⽂件的时候,系统会根据⽂件的情况⾃动创建⼀个FILE结构的变量,并填充其中的信
息,使⽤者不必关⼼细节

⼀般都是通过⼀个FILE的指针来维护这个FILE结构的变量,这样使⽤起来更加⽅便

FILE* pf

在这里插入图片描述

4.3文件的打开和关闭

文件在读写之前应该先打开文件,使用结束应该关闭文件
打开文件的同时都会返回一个FILE*的指针变量指向该文件,也相当于建立了指针和文件的关系

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

在这里插入图片描述

#include <stdio.h>
int main()
{
	FILE* pf = fopen("date.txt", "w");
	FILE* pp = fopen("C:\\Users\\yy\\Desktop\\cod.txt", "w");
	//FILE* pf = fopen(".//..//..//..//game//date.txt","w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	else if (pp == NULL)
	{
		perror("fopen");
		return 1;
	}
	fclose(pf);
	fclose(pp);
	pf = NULL;
	pp = NULL;
	return 0;
}

用w可以创建文件,如果原先没有文件,用r会报错
在打开⽂件的时候,⽂件名字中可以加⼊⽂件所在的路径,可以是相对路径,也可以是绝对路径
在这里插入图片描述

5.文件的顺序读写

5.1顺序读写函数介绍

在这里插入图片描述

int fputc(int c, FILE* stream)

#include <stdio.h>
int main()
{
	FILE* pf = fopen("date.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fputc('a', pf);
	fputc('b', pf);
	fputc('c', pf);
	fputc('c', pf);
	fclose(pf);
	pf=NULL;
	return 0;
}
#include <stdio.h>
int main()
{
	FILE* pf = fopen("date.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	int ch;
	for (ch = 'A';ch <= 'Z';ch++)
	{
		if (fputc(ch, pf) == EOF)
		{
			perror("Error writing to file");
			return -1;
		}
	}
	fclose(pf);
	pf=NULL;
	return 0;
}

在这里插入图片描述

int fgetc ( FILE * stream );

#include <stdio.h>
int main()
{
	FILE* pf = fopen("date.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	int ch=0;
	while ((ch = fgetc(pf)) != EOF)
	{
		printf("%c\n", ch);
	}
	fclose(pf);
	pf = NULL;
	return 0;
}

当 fgetc 的参数是stdin 的时候,函数是在标准输⼊(键盘)上读取字符。
当 fpuct的参数是stdout 的时候,函数是向标准输出(屏幕)上写字符

int ch = getc(stdin);
putc(ch,stdout);

5.2对比一组函数

fputs

输入一行,fgets读取一行
打开文件,文件如果存在,写入会清空原来文件的内容

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

#include <stdio.h>
int main()
{
	FILE* pf = fopen("date.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
	}
	const char s[100] = "i like c";
	fputs(s,pf);
	fputs("hello world", pf);
	fclose(pf);
	pf = NULL;
	return 0;
}

在这里插入图片描述

没有重新打开文件,所以不会清除i like c,并且输入在同一行,如果想换行,加\n
const char s[100] = “i like c\n”;

fgets读取一行,使用时记得改为“r”

char* fgets(char* str, int n, FILE * stream)

#include <stdio.h>
int main()
{
	FILE* pf = fopen("date.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
	}
    char string[100] = { 0 };
	while (fgets(string, 255, pf) != NULL)//读取每一行并打印
	{
		printf("%s", string);
	}
	fclose(pf);
	pf = NULL;
	return 0;
}

fgets使用时中间的数要开大一些,比如有10个数,n=11,前10位读取,最后1位读取\n,
类似fgetc,fputc所有输入输出流

#include <stdio.h>
int main()
{
	char arr[100];
	fgets(arr, 101, stdin);
	fputs(arr, stdout);
	return 0;
}

fscanf

fscanf()函数⽤于从指定流中读取格式化输⼊,并根据指定的格式化字符串将输⼊数据解析为相应的数
据类型。

int fscanf(FILE *stream, const char *format, …)

与scanf不同,fscanf从指定的的流中读取数据,可以是标准输入流,也可以是文件流

示例:
假设有⼀个包含⼀些数据的⽂件"data.txt",内容如下:
John 25 1.7
Emily 23 1.6
zhangsan 28 2.1

#include <stdio.h>
int main()
{
   char name[20];
   int age;
   float height;
   FILE* pf = fopen("date.txt", "r");
   if (pf == NULL)
   {
   	perror("fopen");
   	return -1;
   }
   while (fscanf(pf,"%s %d %f",name,&age,&height)!=EOF)
   {
   	printf("%s %d %.1f\n", name, age, height);
   }
   fclose(pf);
   pf != NULL;
   return 0;
}

fprintf()

int fprintf(FILE *stream, const char *format, …)
将输出写⼊到指定的件流中,可以是标准输出流,也可以是⽂件流

#include <stdio.h>
int main()
{
	char name[20]="zhangsan";
	int age=18;
	float height=1.7;
	FILE* pf = fopen("date.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return -1;
	}
	fprintf(pf, "%s %d %f", name, age, height);
	fclose(pf);
	pf != NULL;
	return 0;
}

在这里插入图片描述

fwrite

功能:函数⽤于将数据块写⼊⽂件流中,是以2进制的形式写⼊的

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

ptr:指向要写⼊的数据块的指针
size:要写⼊的每个数据项的⼤⼩(以字节为单位)
nmemb :要写⼊的数据项的数量

返回值:返回实际写⼊的数据项数量

#include <stdio.h>
int main()
{
	int ptr[5] = { 1,2,3,4,5 };
	FILE* pf;
	pf = fopen("test.txt", "wb");
	if (pf == NULL)
	{
		perror("fopen");
		return -1;
	}
	if (fwrite(ptr, sizeof(int), 5, pf) != 5)
	{
		perror("fwrite");
		return -1;
	}
	fclose(pf);
	pf = NULL;
	return 0;
}

fread
功能:函数⽤于从⽂件中读取数据块,并将其存储到内存缓冲区中

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)

#include <stdio.h>
int main()
{
	FILE* fp = fopen("test.txt", "rb");
	if (fp == NULL)
	{
		perror("fopen");
		return -1;
	}
	int arr[5];
	size_t num=fread(arr, sizeof(int), 5, fp);
	//检查是否读取成功
	if (num != 5)
	{
		if (feof(fp))
		{
			perror("Reached end of file\n");
		}
		else if (ferror(fp))
		{
			perror("Error reading file\n");
		}
	}
	else
	{
		for (int i = 0;i < 5;i++)
		{
			printf("arr[%d]:%d\n",i,arr[i]);
		}
	}
	fclose(fp);
	fp = NULL;
	return 0;
}

sprintf

将带格式的数据转换字符串形式

#include <stdio.h>
int main()
{
	char name[20] = "zhangsan";
	int age = 18;
	double height = 1.8;
	char arr[150] = { 0 };
	sprintf(arr, "%s %d %f", name, age, height);
	printf("%s", arr);
	return 0;
}

sscanf
把字符串中的数据按照格式 提取格式化数据

#include <stdio.h>
int main()
{
	char name[20] = "zhangsan";
	int age = 18;
	double height = 1.8;
	char arr[150] = { 0 };
	sprintf(arr, "%s %d %.1f", name, age, height);
	printf("%s\n", arr);
	char name2[20] = "";
	int age2 = 0;
	double height2 = 0;
	sscanf(arr, "%s %d %f", name2, &age, &height);
	printf("%s\n", name2);
	printf("%d\n", age);
	printf("%.1f\n", height);
	return 0;
}

6.文件的随机读写

6.1freek

功能:函数⽤于设置⽂件流的位置指针,即将⽂件流的当前位置移动到指定位置。这个函数通常与ftell() ⼀起使⽤,可以在⽂件中定位到特定的位置进⾏读取或写⼊操作。

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

stream :指向FILE类型结构体的指针,指定了要设置位置指针的⽂件流
offset :相对于origin 参数的偏移量,以字节为单位
origin :指定偏移量的起始位置,可以是以下值之⼀:

SEEK_SET
:从⽂件起始位置开始偏移。

SEEK_CUR
:从当前位置开始偏移。

SEEK_END
:从⽂件末尾位置开始偏移

返回类型
int :如果成功设置位置指针,则返回0;如果发⽣错误,返回⾮零值

#include <stdio.h>
int main()
{
	int position=0;
	FILE* pf = fopen("date.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return -1;
	}
	if (fseek(pf,-4,SEEK_END)!= 0)
	{
		perror("fseek");
		return -1;
	}
	position = ftell(pf);
	if (position == -1)
    {
	    perror("ftell");
	    return -1;
    }
    fclose(pf);
    pf=NULL;
	printf("Current position :%d", position);
	
	
	return 0;
}

6.2ftell

long ftell(FILE *stream);

功能:函数⽤于获取⽂件流的当前位置,即指针相对于⽂件起始位置的偏移量(以字节为单位)
返回值:long :返回当前位置相对于⽂件起始位置的偏移量(以字节为单位)。如果发⽣错误,返回
(-1)

#include <stdio.h>
int main()
{
	FILE* pf;
	pf = fopen("date.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return -1;
	}
	int position = ftell(pf);
	if (position == -1)
	{
		perror("ftell");
		return -1;
	}
	printf("current position %d\n", position);
	fgetc(pf);
	position = ftell(pf);
	//读取一个字符,光标向后偏移一位
	printf("current position %d\n", position);
	fclose(pf);
	pf = NULL;
	return 0;
}

6.3rewind

让文件指针回到起始位置

void rewind(FILE* stream);

7.文件读取结束的标志

7.1被错误使用的feof

feof不直接用于判断文件是否读取结束,而是读取结束后,判断是否遇到了文件末尾。因为读取结束有可能遇到了其他的错误。

如果文件到了末尾,返回非0值,否则返回0

#include <stdio.h>
int main()
{
	FILE* fp = fopen("date.txt", "r");
	if (fp == NULL)
	{
		perror("fopen");
		return -1;
	}
	int ch;
	while (ch = fgetc(fp) != EOF)
	{
		printf("%d\n", ch);
	}
	if (feof(fp))
	{
		printf("文件遇到末尾,无错误\n");
	}
	else
	{
		perror("文件读取发生其他错误\n");
	}
	fclose(fp);
	fp = NULL;
	return 0;
}

perror

int ferror(FILE *stream)

功能:函数⽤于检查⽂件流的错误标志,以确定⽂件读写操作是否发⽣了错误。
返回值:如果⽂件流的错误标志已经设置,则返回⾮零值;否则返回0

#include <stdio.h>
int main()
{
	FILE* fp = fopen("date.txt", "w");
	if (fp == NULL)
	{
		perror("fopen");
		return -1;
	}
	fputs("abc", fp);
	if (ferror(fp))
	{
		printf("读写错误\n");
	}
	fclose(fp);
	fp = NULL;
	return 0;
}

8.文件缓冲区

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值