c语言 文件详解(fgetc,fgets,fscanf,fread等)

一: 为什么使用文件

      比如说我们想创建一个通讯录系统,当程序跑起来时,我们会从键盘上输入数据,此时数据是保存在内存中的,但是当程序结束时,通讯录中的数据就丢失了,等下次在运行通讯录的时候,我们还得重新输入信息。
使用文件我们可以将数据直接存放在电脑的硬盘上,做到了数据的持久化

二: 什么是文件

2.1 文件

磁盘上的文件是文件。
但是在程序设计中,我们一般谈的文件有两种: 程序文件 数据文件 (从文件功能的角度来分类的)。
程序文件: 
                 源程序文件(后缀位.c)
                 目标文件(windows环境后缀为.obj)
                 可执行文件(windows环境后缀为.exe)
数据文件: 程序运行需要从中读取数据的文件,或者输出内容的文件

2.2 文件名

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

三:文件的打开和关闭

3.1 文件指针

缓冲文件系统中,关键的概念是 文件类型指针 ,简称 文件指针
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名FILE .
下面我们可以创建一个文件指针变量:
FILE* pf;//文件指针变量
定义 pf 是一个指向 FILE 类型数据的指针变量。可以使 pf 指向某个文件的文件信息区(是一个结构体变量)。通过该文件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够找到与它关联 的文件.

3.2 文件的打开和关闭

文件在读写之前应该先 打开文件 ,在使用结束之后应该 关闭文件
在编写程序的时候,在打开文件的同时,都会返回一个 FILE* 的指针变量指向该文件,也相当于建立了指针和文件的关系。
ANSIC 规定使用 fopen 函数来打开文件, fclose 来关闭文件.
//打开文件    第一个参数为文件名   第二个参数为打开方式
FILE* pf=fopen(const char* filename,const char* mode);
//关闭文件
FILE* pf=fclose(FILE* stream);

打开方式如下:

文件使用方式
含义
如果指定文件不存在
“r” (只读)
为了输入数据,打开一个已经存在的文本文件
出错
“w” (只写)
为了输出数据,打开一个文本文件
建立一个新的文件
“a” (追加)
向文本文件尾添加数据
建立一个新的文件
“rb” (只读)
为了输入数据,打开一个二进制文件
出错
"wb” (只写)
为了输出数据,打开一个二进制文件
建立一个新的文件
“ab” (追加)
向一个二进制文件尾添加数据
出错
"r+” (读写)
为了读和写,打开一个文本文件
出错
"w+”(读写)
为了读和写,建议一个新的文件
建立一个新的文件
“a+” (读写)
打开一个文件,在文件尾进行读写
建立一个新的文件
“rb+” (读写)
为了读和写打开一个二进制文件
出错
“wb+” (读写)
为了读和写,新建一个新的二进制文件
建立一个新的文件
“ab+” (读写)
打开一个二进制文件,在文件尾进行读和写
建立一个新的文件

实例:

/* fopen fclose example */
#include <stdio.h>
int main ()
{
  FILE * pFile;
  //打开文件
  pFile = fopen ("myfile.txt","w");
  //文件操作
  if (pFile!=NULL)
 {
    fputs ("fopen example",pFile);
    //关闭文件
    fclose (pFile);
    pFile=NULL;

 }
  return 0;
}

四:文件的顺序读写

4.1 文件的读写概念

顺序读写就是从文件的开头按顺序进行读写操作

首先我们要明白的概念是什么:

4.2 流的介绍

流 是一个高度抽象的概念,我们可以把它理解为信息流,数据流

比如说程序员想把数据放到屏幕上,文件里,硬盘上等等,需要打开对应设备的流,把信息放在流里, 流再把数据放到相应位置.

我们向文件里写信息,需要先打开文件,返回了一个FILE*的指针,这个指针就是指向的流,这样我们就可以向文件里输入信息了

这时,有同学就会问,好像我们用printf scanf时也没有打开什么东西,直接用就可以在屏幕上输入输出信息那是怎么回事?

那是因为c语言程序只要运行起来就默认打开了三个流:

  • 标准输入流:stdin
  • 标准输出流:stdout
  • 标准错误流:stderr

当我们用scanf从键盘读数据时就是从标准输入流里读数据

当我们用printf从屏幕打印数据时就是从标准输出流里输出数据

例如: 我们不用printf向屏幕输出数据

下面是文件常用的读写函数:
 

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

4.3 函数介绍

  字符输入函数 fgetc()

int fgetc ( FILE * stream );

  从文件流中读取一个字符,读取正常的话,返回所读字符的ASCLL值,若读取到文件末尾,读取失败,则返回EOF

使用实例:

#define  _CRT_SECURE_NO_WARNINGS 1		
#include<stdio.h>
int main()
{
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1; //异常返回
	}
	int ch = fgetc(pf);
	printf("%c", ch);

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

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

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

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

字符输出函数fputc()

int fputc ( int character, FILE * stream );

将character写到文件流中,写入成功返回character的ASCLL,失败则返回EOF

使用实例:  把二十六个字母写到文件中

#define  _CRT_SECURE_NO_WARNINGS 1		
#include<stdio.h>
int main()
{
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1; //异常返回
	}
	for (char i = 'a'; i <= 'z'; i++)
	{
		fputc(i, pf);
	}
	fclose(pf);
	pf = NULL;
	return 0;
}

文本行输入函数fgets():

char * fgets ( char * str, int num, FILE * stream );

从流中读取num-1个字符放到str所指空间中,并返回str首地址,如果读取过程中遇到\n就停止读取

例如:

假设data.txt中内容如下

#define  _CRT_SECURE_NO_WARNINGS 1		
#include<stdio.h>
int main()
{
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1; //异常返回
	}
	int arr[20] = { 0 };
	fgets(arr, 10, pf);
	printf("%s\n", arr);
	fclose(pf);
	pf = NULL;
	return 0;
}

答案可见虽然我们想提取文件中的9个字符,但是遇到\n时提取就结束了

格式化输出函数fprintf():

int fprintf ( FILE * stream, const char * format, ... );

把格式化数据写到流中

#define  _CRT_SECURE_NO_WARNINGS 1		
#include<stdio.h>
 struct S
{
	float f;
	char c;
	int n;
};

int main()
{
	struct S s = { 3.14f, 'w', 100 };

	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	fprintf(pf, "%f-%c-%d", s.f, s.c, s.n);

	fclose(pf);
	pf = NULL;

	return 0;
}

格式化输入fscanf():

int fscanf ( FILE * stream, const char * format, ... );

把带格式的数据从流中读出来 

#define  _CRT_SECURE_NO_WARNINGS 1		
#include<stdio.h>
struct S
{
	float f;
	char c;
	int n;
};

int main()
{
	struct S s = {0};

	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	fscanf(pf, "%f-%c-%d", &(s.f), &(s.c), &(s.n));
	printf("%f-%c-%d\n", s.f, s.c, s.n);

	fclose(pf);
	pf = NULL;

	return 0;
}

补充知识点:

ssprintf(): 将格式化数据放到字符串中
 

int sprintf ( char * str, const char * format, ... );

 sscanf():将带有格式的字符串提取成格式化数据

		
#include<stdio.h>
 struct S
{
	float f;
	char c;
	int n;
};

int main()
{
	struct S s = { 3.14f, 'c', 100 };
	char arr[100] = { 0 }; 
    //把格式化数据写成字符串,并将首地址传给arr
	sprintf(arr, "%f-%c-%d", s.f, s.c, s.n);
	printf("%s\n", arr);

	struct S tmp = { 0 };
    //把arr中的字符串提取成格式化数据
	sscanf(arr, "%f-%c-%d", &(tmp.f), &(tmp.c), &(tmp.n));
	printf("%f\n", tmp.f);
	printf("%c\n", tmp.c);
	printf("%d\n", tmp.n);

	return 0;
}

二进制输出函数fwrite():

从ptr所指空间中提取count个大小为size的数据到流中

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

ptr:目标空间

size:每个元素的大小

count:要写入数据的个数

#include<stdio.h>
//二进制的方式写进文件
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	//写文件
	FILE*pf = fopen("data.txt", "wb");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//二进制的写文件
	fwrite(arr, sizeof(arr[0]), sizeof(arr)/sizeof(arr[0]), pf);

	fclose(pf);
	pf = NULL;

	return 0;
}

可见数据被成功写入文件中,并且是我们看不懂的二进制形式

二进制输入函数fread():

size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );

从流中读取count个大小为size的数据到ptr所指空间

刚刚我们以二进制形式将数据写到文件中,我们同样可以以二进制形式读取出来

#include<stdio.h>
//二进制的方式读取文件
int main()
{
	int arr[10] = {0};
	FILE* pf = fopen("data.txt", "rb");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//二进制的读文件
	fread(arr, sizeof(arr[0]), sizeof(arr) / sizeof(arr[0]), pf);

	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}

	fclose(pf);
	pf = NULL;

	return 0;
}

五:文件的随机读写

fseek()

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

  • SEEK_SET      文件开头
  • SEEK_CUR     文件指针当前位置
  • SEEK_END      文件末尾

ps: offest负数表示向前移,整数表示向后移

ftell()

long int ftell ( FILE * stream );

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

rewind()

void rewind ( FILE * stream );

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

六:文件读取结束的判定

  •  fgetc  判断是否为EOF
  •  fgets 判断返回值是否为NULL
  •  fread 判断返回值是否小于实际要读的个数

ps:feof()是检测流上的文件结束符的函数,如果文件结束,则返回非0值,否则返回0

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

张呱呱_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值