[c语言]——文件操作

c语言文件操作

什么是文件

解释:磁盘上的文件是文件。包含程序文件和数据文件。

程序文件:包括后缀为.c文件,和可执行文件(.exe文件&&.obj文件)。
数据文件:程序从文件中读取,或者输出到文件,比如存放数据的txt文本文件。

文件名

比如:c:\code\test.text
文件名:文件名+文件主干+文件后缀。

文件类型

文件类型:有文本文件(数据的组织形式)和二进制文件(以二进制存放的文件)。

文件指针

缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”。

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

FILE* pf;//文件指针变量

FILE转到定义如下

#ifndef _FILE_DEFINED
struct _iobuf {
        char *_ptr;
        int   _cnt;
        char *_base;
        int   _flag;
        int   _file;
        int   _charbuf;
        int   _bufsiz;
        char *_tmpfname;
        };
typedef struct _iobuf FILE;
#define _FILE_DEFINED
#endif  /* _FILE_DEFINED */

文件缓冲区

文件缓冲区:有输入缓冲区和输出缓冲区
在这里插入图片描述

我们对于程序数据和磁盘数据的交换的时候不是直接进行交换的,他们之间存在了一个数据缓冲区

证明存在输入缓冲区

下面这段代码很多朋友可能不知道,当你输入一串字符串,假设输入abcdef时,一个回车,你以为接下来要确定输入Y/N时这个代码实际直接结束了,他跳过了输入这个字符的环节(感兴趣的同学试一试)

int main()//验证输入缓冲区
{
	int ch;
	char arr[10] = { 0 };
	printf("请输入密码\n");
	scanf("%s", arr);
	printf("请确认密码(Y/N)\n");	
	ch = getchar();
	if (ch == 'Y')
	{
		printf("y");
	}
	else
	{
		printf("n");
	}
	system("pause");
	return 0;
}

为什么呢?让我来告诉你
在这里插入图片描述
那我们有没有解决这个bug的方案呢?
有,在vs2008下我们可以使用fflush函数来清空输入缓冲区,但是这个函数在高级编译器(vs2013)以后就禁止使用了。但我们可以自己清空缓冲区

while (getchar() != '\n')//在getchar()之前获取完缓冲区中所有的字符
	{
		;
	}

证明存在输出缓冲区

此代码必须在Linux下验证
如下代码很多同学以为会没秒输出一个a,但是事实上他输出一个后就不在输出了,这其实也就证明输出缓冲区是存在的,只有当缓冲区的数据满时,他才会一次性打印在屏幕上。

int main()
{
	while (1)
	{
		printf("a");
		Sleep(1);//linux下停止1s
	}
	return 0;
}

摘要:数据缓冲区的大小是由编译器决定的

文件操作函数

下面代码会使用此结构体

struct S
{
	char name[20];
	int age;
	float f;
};

1.fopen函数(打开一个文件)和fclose(关闭一个文件)

FILE *fopen( const char *filename, const char *mode );//参数:文件名称,打开方式

int fclose( FILE *stream )//参数:文件指针

打开方式介绍(经常使用的)

文件使用方式含义如果指定文件不存在
“r”(只读)为了输入数据出错
“w”(只写)为了输出数据创建一个新文件
“a”(追加)为文件尾部添加数据出错
“rb” (二进制)为了输入数据打开二进制文件出错
“wb” (二进制)为了输出数据打开二进制文件创建一个新文件

函数使用:

#include<stdio.h>
int main()
{
	FLIE* p;//定义一个文件指针
    p = fopen("test.txt","w");//假设我们的工程下有一个叫test。txt的文件,打开方式为只读
	if(p != NULL);
	{
		//操作文件
		fclose(p);
	}
	return 0;
}

2.字符输入函数fgetc

int fgetc( FILE *stream );//参数:为文件指针
int main()
{
	FILE* pf = fopen("test.txt", "r");
	int ch = 0 ;
	if(pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	//读文件
	ch = fgetc(pf);//向文件输入一个字符	
	printf("%c\n", ch);
	fclose(pf);
	
	pf = NULL;
	return 0;
}

3.字符输出函数fputc

int fputc( int c, FILE *stream );参数:为要输出(输出到文件)那个字符,和文件指针
#include <stdio.h>
#include <errno.h>//报错判断头文件
#include <string.h>
int main()
{

	FILE* pf = fopen("test.txt", "w");
	if(pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	//写文件
	fputc('b', pf);//向文件写入一个字符
	fputc('i', pf);
	fputc('t', pf);
	
	fclose(pf);
	pf = NULL;
	return 0;
}

4.文本行输出函数fputs

int fputs( const char *string, FILE *stream );参数为要输出的字符串和文件流

返回值:Each of these functions returns a nonnegative value if it is successful. On an error, fputs returns EOF, and fputws returns WEOF(成功返回一个非负值)

int main()
{
	FILE* pf = fopen("test.txt", "w");
	int ch = 0 ;
	if(pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	fputs("hello world", pf);
	fclose(pf);
	pf = NULL;
	return 0;
}

5.文本行输入函数fgets

char *fgets( char *string, int n, FILE *stream );//参数为数据要输入的位置,读取的最大字符数,文件指针

返回值: 这些函数都返回字符串。
返回NULL表示错误或文件结束条件。使用feof或ferror来确定是否发生了错误(后面介绍)

int main()
{
	FILE* pf = fopen("test.txt", "r");
	char arr[20] = {0};	
	if(pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	fgets(arr, 20, pf);
	printf("%s", arr);
	
	fclose(pf);
	pf = NULL;
	return 0;
}

6.格式化输入函数fscanf

int fscanf( FILE *stream, const char *format [, argument ]... );//参数是文件指针和格式控制字符串

函数返回值:每个函数返回成功转换和分配的字段数;返回值不包括已读取但未分配的字段。返回值0表示没有分配任何字段。如果发生错误,或者在第一次转换之前到达了文件流的末尾,则返回值为fscanf的EOF或fwscanf的WEOF

struct S
{
	char name[20];
	int age;
};
int main()
{
	struct S s = {0};
	FILE* pf = fopen("text.txt","r");
	if(pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	//读
	fscanf(pf, "%s %d", s.name, &(s.age));	
	printf("%s %d\n", s.name, s.age);
	
	fclose(pf);
	pf = NULL;
	return 0;
}

7.格式化输出函数fprintf

int fprintf( FILE *stream, const char *format [, argument ]...);//参数是文件指针和格式控制字符串

函数返回值:返回写入的字节数。返回写入的宽字符数。当出现输出错误时,每个函数返回一个负值。

int main()
{
	struct S s = {"zhangsan", 20};
	
	FILE* pf = fopen("text.txt","w");
	if(pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	//写
	fprintf(pf, "%s %d", s.name, s.age);//输出到pf

	fclose(pf);
	pf = NULL;
	return 0;
}

拓展:sscanf和sprintf函数

问题:将一个结构体中%s,%d,%f的数据都以%s输出怎么办?

来看sprintf函数

int sprintf( char *buffer, const char *format [, argument] ... );
//参数:输入到哪个数组,数据格式,转换哪些数据
int main()
{
	struct S s = { "zhangsan", 20, 3.14f };	
	char buf[30] = { 0 };	

	sprintf(buf, "%s %d %f", s.name, s.age, s.f);
	printf("%s\n", buf);	
	
	system("pause");
	return 0;
}

问题:我又要把刚才的数据用原来的方式提取出来?

int sscanf( const char *buffer, const char *format [, argument ] ... );
//参数:输入到哪个数组,数据格式,转换哪些数据

在刚才的基础上有了下面的代码

int main()
{
	struct S s = { "zhangsan", 20, 3.14f };
	struct S tmp = {0};
	char buf[30] = { 0 };	

	sprintf(buf, "%s %d %f", s.name, s.age, s.f);
	printf("%s\n", buf);
	sscanf(buf, "%s %d %f", tmp.name, &(tmp.age), &(tmp.f));
	printf("%s %d %f\n", tmp.name, tmp.age , tmp.f);	
	
	system("pause");
	return 0;
}

面试题:scanf,printf,fprintf,fscanf,sscanf,sprintf的区别

在这里插入图片描述

exe程序开始运行 默认打开 stdin stdout stderr
scanf针对标准输入流的格式化的输入函数
printf针对标准输出流的格式化输出函数
fscanf针对所有输入流的格式化输入函数
fprintf针对所有输出流的格式化输出函数
sscanf从内存中格式化的转化成字符串
sprintf从字符串转换为格式化的数据

8.二进制输入 函数fread

size_t fread( void *buffer, size_t size, size_t count, FILE *stream )

参数第一个为数据存储的位置,第二个为目标字节大小,第三个为元素的最大数量,第四个参数为文件流

返回值:fread返回实际读取的完整项的数量

int main()
{
	struct S s = {0};
	FILE* pf = fopen("test.txt", "rb");
	if(pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	//读操作
	fread(&s, sizeof(struct S), 1, pf);
	printf("%s %d\n", s.name, s.age);
	fclose(pf);
	pf = NULL;
	return 0;
}

9.二进制输出 函数fwrite

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

参数第一个为数据存储的位置,第二个为目标字节大小,第三个为元素的最大数量,第四个参数为文件流

返回值:fwrite返回实际写入的完整项的数量,如果发生错误,该数量可能小于计数。

int main()
{
	struct S s = {"张三", 20};
	FILE* pf = fopen("test.txt", "wb");
	if(pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}
	//写操作
	fwrite(&s, sizeof(struct S), 1, pf);
	fclose(pf);
	pf = NULL;
	return 0;
}

10.根据偏移量定位指针的fseek函数(了解)

int main()
{
	FILE* pf = fopen("test.text", "r");
	printf("%c\n", fgetc(pf));
	fseek(pf, 3, SEEK_CUR);//调整文件指针的位置
	//fseek(pf, -2, SEEK_END);//调整文件指针的位置(从后往前)
	
	printf("%c\n", fgetc(pf));
	printf("%c\n", fgetc(pf));	

	system("pause");
	return 0;
}

11.文件结束判断函数feof

被错误使用的feof

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

文件是否读取结束判断方法:

  1. 文本文件读取是否结束,判断返回值是否为EOF (fgetc),或者NULL(fgets)
    例如:
    fgetc判断是否为EOF.
    fgets判断返回值是否为NULL.

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

牢记feof是判断文件的结束方式的

int main(void)
{
    int c; // 注意:int,非char,要求处理EOF
    FILE* fp = fopen("test.txt", "r");
    if(!fp) {
        perror("File opening failed");
        return EXIT_FAILURE;
   }
 //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);
}

一些经常使用的文件操作函数笔者就整理了这些,如果不全的地方欢迎大家补充说明

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值