C文件操作

C语言的内容已经差不多要结束了。今天我们了解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>
#include<stdlib.h>
int main()
{
	//00000000 00000000 00100111 00010000
	//   00       00       27       10
	int a = 10000;
	//打开文件
	FILE* pf = fopen("data.txt", "wb");
	if (NULL == pf)
	{
		printf("fopen fail\n");
		exit(-1);
	}
	fwrite(&a, sizeof(int), 1, pf);//以二进制的形式写到文件中
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

在这里插入图片描述


4 文件的打开和关闭

4.1 流

我们程序的数据需要输出到各种外部设备,也需要从各种外部设备获取数据,不同的外部设备的输入输出操作各不相同。为了方便程序员对各种设备进行方便的操作,我们抽象出了的概念。

C程序针对文件、画面、键盘等的数据输入输出操作都是通过流操作的

一般情况下,我们要想向流里写数据,或者从流中读取数据,都是要打开流,然后操作

在这里插入图片描述


4.2 标准流

有一个问题需要思考一下。我们已经学习了printf和scanf函数,那为什么以前在使用这两个函数的时候没有打开流呢?

那是因为C语言程序在启动的时候,默认打开了3个流:

stdin标准输入流,在大多数的环境中从键盘输入,scanf函数就是从标准输入流中读取数据。

stdout标准输出流,大多数的环境中输出至显示器界面,printf函数就是将信息输出到标准输出流中。

stderr标准错误流,大多数环境输出到显示器界面。

stdin、stdout、stderr三个流的类型是:FILE* ,通常称为文件指针

C语言中,就是通过FILE*的文件指针来维护流的各种操作的。

4.3 文件指针

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

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

每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息。

一般都是通过一个FILE结构的指针来维护这个FILE结构的变量

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

定义pf是一个指向FILE类型数据的指针变量,可以使pf指向某个文件的文件信息区(是一个结构体变量)。通过该文件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够间接找到与它关联的文件

在这里插入图片描述


4.4 文件的打开与关闭

文件在读写之前应该先打开文件,在使用结束之后应该关闭文件

//fopen是用来打开文件的
//filename表示文件名
//mode表示文件的打开方式
FILE* fopen(const char* filename, const char* mode);
//fclose是用来关闭文件的
//成功关闭文件,返回0
//文件关闭失败,返回EOF
int fclose(FILE* stream);

在这里插入图片描述

5. 文件的顺序读写

5.1 顺序读写函数介绍

在这里插入图片描述

先来搞清楚内存和文件之间的关系。

在这里插入图片描述


5.1.1 fgetc函数介绍

//从流得到字符
//成功的话,返回读取到的字符,提升为整型值(ASCII码值)
//失败的话或者遇到文件结束标志,发生读取错误,就返回EOF
int fgetc(FILE* stream);

在这里插入图片描述
在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>
int main()
{
	//以写的方式打开文件
	FILE* pf = fopen("test.txt", "w");
	if (NULL == pf)
	{
		printf("fopen fail\n");
		exit(-1);
	}
	//写文件
	char ch = 0;
	for (ch = 'a'; ch <= 'z'; ch++)
	{
		fputc(ch, pf);
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

在这里插入图片描述

5.1.2 fputc函数介绍

//写字符到流中
//被写的字符提升为整型int,当写这个字符的时候,内部的值转换为一个
//unsigned char类型
//stream是一个指向标识输出流的文件对象的指针
//成功的话,返回写下的字符
//写入错误,返回EOF
int fputc(FILE* stream);

在这里插入图片描述
在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>
int main()
{
	//以读的方式打开文件
	FILE* pf = fopen("test.txt", "r");
	if (NULL == pf)
	{
		printf("fopen fail\n");
		exit(-1);
	}
	//读文件
	int ch = 0;
	while ((ch = fgetc(pf)) != EOF)
	{
		//写到标准输出流中,也就是屏幕上
		fputc(ch, stdout);
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

5.1.3 fgets函数介绍

//str是一个指向被拷贝字符串的字符数组的指针
//num被拷贝字符的最大数量
//stream是一个指向标识输入流的文件对象的指针
//成功的话,返回一个字符串
//遇到文件结束标志,设置一个错误标志
//如果在读取任何字符之前,遇到了文件结束标志,返回一个空指针,str指向的内容仍然不变
//读取错误,设置错误标志并返回一个空指针,但是通过str指向的内容也许会改变
char* fgets(char* str,int num,FILE* stream);

在这里插入图片描述
在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>
int main()
{
	//以读的方式打开文件
	FILE* pf = fopen("test.txt", "r");
	if (NULL == pf)
	{
		printf("fopen fail\n");
		exit(-1);
	}
	//读文件
	char ch[10] = { 'a'};
	//读取num-1个字符
	char* pch = fgets(ch, 6, pf);
	printf("%s\n", pch);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

在这里插入图片描述
在这里插入图片描述

5.1.4 fputs函数介绍

//str写到流中的字符串的内容
//stream是一个指向标识输出流的文件对象的指针
//成功的话,返回一个非负数
//失败的话,返回EOF并设置错误标志
int fputs(const char* str,FILE* stream);

在这里插入图片描述
在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "w");
	if (NULL == pf)
	{
		perror("fopen");
		exit(-1);
	}
	//写文件
	fputs("hello everyone\n", pf);
	fputs("hello world\n", pf);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

在这里插入图片描述

5.1.5 fscanf函数介绍

//stream是一个指向从标识输入流读取数据的文件对象的指针
//成功的话,该函数返回成功填充参数列表的项数
//失败的话,返回EOF
int fscanf(FILE* stream,const char* format,...);

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
	int num = 0;
	char name[10] = { 0 };
	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (NULL == pf)
	{
		perror("fopen");
		exit(-1);
	}
	//读文件
	fscanf(pf, "%d %s", &num, name);
	printf("%d %s\n", num, name);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

在这里插入图片描述

5.1.6 fprintf函数介绍

//stream是一个指向标识输出流的文件对象的指针
//成功的话,返回写下字符的总个数
//写入发生错误,设置错误标志并且返回一个负数
int fprintf(FILE* stream,const char* format,...);

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
	int num = 18;
	char name[10] = "小美";
	//打开文件
	FILE* pf = fopen("test.txt", "w");
	if (NULL == pf)
	{
		perror("fopen");
		exit(-1);
	}
	//写文件
	fprintf(pf, "%d %s\n", num, name);
	printf("%d %s\n", num, name);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

在这里插入图片描述

5.1.7 fread函数介绍

//ptr指向了一个内存块,大小至少为size*count个字节
//size读取每个元素的大小,单位是字节
//count元素的个数
//stream是一个指向输入流的文件对象的指针
//成功读取的话,返回的是元素的总个数
size_t fread(void* ptr,size_t size,size_t count,FILE* stream);

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
	int arr[5] = { 0 };
	//打开文件
	FILE* pf = fopen("test.txt", "rb");//以二进制的形式写
	if (NULL == pf)
	{
		perror("fopen");
		exit(-1);
	}
	//读文件
	fread(arr, sizeof(int), 5, pf);
	for (int i = 0; i < 5; i++)
	{
		printf("%d ", arr[i]);
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

5.1.7 fwrite函数介绍

//ptr指向了一个被写下元素的数组
//size是被写下每个元素的大小,单位是字节
//count元素的个数
//stream指向了一个输出流的文件对象的指针
//成功写入的话,返回的是元素的总个数
size_t fwrite(const void* ptr,size_t size,size_t count,FILE* stream);

在这里插入图片描述
在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
	int arr[5] = { 1,2,3,4,5 };
	//打开文件
	FILE* pf = fopen("test.txt", "wb");//以二进制的形式写
	if (NULL == pf)
	{
		perror("fopen");
		exit(-1);
	}
	//写文件
	fwrite(arr, sizeof(int), 5, pf);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

在这里插入图片描述
在这里插入图片描述

5.2 sscanf 和 sprintf

scanf/fscanf/sscanf
printf/fprintf/sprintf

这些函数中只有sscanf,sprintf没有接触过,接下来我们就来看看这两个函数。

sprintf函数是将格式化的数据转换成字符串
sscanf函数是从字符串提取格式化的数据

在这里插入图片描述
在这里插入图片描述

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
	int num = 100;
	float score = 3.14f;
	char name[10] = "小强";

	char str[100] = { 0 };
	//将格式化的数据转换成字符串
	sprintf(str, "%d %f %s\n", num, score, name);
	printf("字符串的形式:%s\n", str);
	int num1 = 0;
	float score1 = 0;
	char name1[10] = { 0 };
	//从字符串中提取格式化的数据
	sscanf(str, "%d %f %s\n", &num1, &score1, name1);
	printf("格式化的形式:%d %f %s\n", num1, score1, name1);
	return 0;
}

6 文件的随机读写

6.1 fseek 和 ftell

//offset相对于起始地址的偏移量
//origin文件的起始地址
//origin有3个取值:SEEK_SET(文件的起始地址),SEEK_CUR(文件当前位置),SEEK_END(文件的末尾)
//fseek文件的随机读写
int fseek(FILE* stream,long int offset,int origin);

在这里插入图片描述
在这里插入图片描述

//返回的是相对于文件起始地址的偏移量
long int ftell(FILE* stream);

在这里插入图片描述
在这里插入图片描述

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
	//以读的方式打开文件
	FILE* pf = fopen("test.txt", "r");
	if (NULL == pf)
	{
		perror("fopen");
		exit(-1);
	}
	//读文件
	int ch = fgetc(pf);//a
	printf("%c\n", ch);
	//文件的随机读写,移动光标的位置
	fseek(pf, 2, SEEK_CUR);
	ch = fgetc(pf);
	printf("%c\n", ch);//d
	//计算相对于文件起始地址的偏移量
	long int r = ftell(pf);
	printf("%ld\n", r);//4
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

6.2 rewind

//文件指针的位置回到文件的起始地址
void rewind(FILE* stream);

在这里插入图片描述

7 文件读取结束的判定

. 被错误使用的feof

注意:在文件读取过程中,不能用feof函数的返回值直接来判断文件是否结束

feof作用是:当文件读取结束的时候,判断文件读取结束的原因是否是遇到了文件结束标志结束

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

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

8 文件缓冲区

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

在这里插入图片描述


评论 39
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值