C语言文件操作

目录

1. 为什么使⽤⽂件?

2. 什么是⽂件?

2.1、数据文件

2.2、程序文件

3. ⼆进制⽂件和⽂本⽂件?

4. ⽂件的打开和关闭

4.1、流和标注流

4.1.1流

4.1.2标准流

4.2、文件指针

4.3 ⽂件的打开和关闭

5.⽂件的顺序读写

5.1、fgetc函数

5.2、fputc函数

5.3、fputs函数

5.4、fgets函数

5.5、fprintf函数

5.6、fscanf函数

5.7、fwrite函数

5.8、fread函数

5.9、 sprintf函数

5.10、sscanf函数

6. ⽂件的随机读写

6.1、fseek函数

6.1.1、SEEK_SET文件开头

6.1.2、SEEK_CUR文件指针的当前位置

6.1.3、SEEK_END文件结束 *

6.2、ftell函数

6.3 rewind函数

7. ⽂件读取结束的判定

7.1、字符串文本例子

7.2、二进制文本例子

8.、⽂件缓冲区


1. 为什么使⽤⽂件?

我们所写的数据是保存在内存中的,并不是直接在磁盘保存,通过文件可以保存我们在内存中所需要的内容,没用文件的话,我们就无法保存文件。也就是说我们要将数永久保存,就可以使用文件。

2. 什么是⽂件?

文件分为两种:1、数据文件。2、程序文件

2.1、数据文件

数据文件是我们所需要读取的内容,在一个程序中,我们会读取里面的数据,读取出来的内容是数据文件

2.2、程序文件

比如:可执行文件(.exe),源程序文件(.c),目标文件(.obj)

有这些后缀的都是程序文件

3. ⼆进制⽂件和⽂本⽂件?

文本文件:把内容用字符来保存到文件当中,使用ascll字符的形式进行存储的,计算机保存数据的时候要进行转译

二进制文件:内容直接用二进制形式保存到文件当中,计算机可以认识并且直接读取的,不利于人类看

例题:目前大家无需知道代码是什么意思,这只是演示把二进制文件打开方式跟我们看见的内容是什么样子的。

int main()
{
	int a = 1000;
	FILE* pf = fopen("data.txt", "wb");//打开data.txt,如果目录没用就新建一个
	fwrite(&a, 4, 1, pf);
	fclose(pf);
	pf = NULL;
	return 0;
}

4. ⽂件的打开和关闭

4.1、流和标注流

4.1.1流

程序需要使用各种的外部设备,也需要输出给外部设备,在与不同的设备进行互相传输的时候,为了方便程序员对各种设备进行调试,我们抽象出流这个概念,我们可以把流想象处流淌着字符的河

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

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

4.1.2标准流

标准流是我们C语言启动时候默认打开的

stdin-标准输入流,绝大情况从键盘输入,scanf函数就是从标准输入流进行读取数据的

stdout-标准输出流,绝大情况输出至显示器上,printf函数就是讲信息输出到标准输出流上

stderr-标准错误流,绝大情况输出至显示器上

三个流的类型是FILE*,通常指文件指针,通过FILE*来维护流的各种操作

4.2、文件指针

FILE*用与维护各种流,我们需要使用哪种流,通过创建FILE*指针我们就可以实现我们所要的效果,是输出屏幕还是从各种外部设备输入,还是打开关闭文件,写入文件等等操作

在使用每个文件的时候,就会创建一个文件信息区,用于存放文件的相关内容,这些内容都会保存到一块结构体中FILE*

struct _iobuf {
	char* _ptr;
	int _cnt;
	char* _base;
	int _flag;
	int _file;
	int _charbuf;
	int _bufsiz;
	char* _tmpfname;
};
typedef struct _iobuf FILE;//把结构体重命名为FILE

不同编译器的文件信息区大小不一样,我们可以通过FILE来维护这个结构的变量

比如我们创建一个FILE*指针

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

pf是指向FILE类型数据的变量,可以使pf指向某个文件的文件信息区,从而让我们可以使用这个文件,也就是说FILE* 可以帮我我们找到我们需要使用的文件

4.3 ⽂件的打开和关闭

fopen是一个打开文件的函数
fclose是一个关闭文件的函数
fopen
FILE * fopen ( const char * filename, const char * mode );
fclose
int fclose ( FILE * stream );

mode的意思是我们是读还是写,怎么读,怎么写。

文件使用方式

含有

如果指定文件不存在

“r”   (只读)

为了输入,打开一个存在的文本文件

出错

“w” (只写)

为了输出,打开一个文本文件

创建新文件

“a” (追加)

向文本文件的末尾添加数据

创建新文件

“rb”  (只读)

输入文件,打开一个二进制文件

出错

“wb”(只写)

输出文件,输出一个二进制文件

创建新文件

“ab” (追加)

向一个二进制文件的末尾添加数据

创建新文件

“r+” (读写)

为了读和写,打开一个文件

错误

“w+”(读写)

为了读和写,新建一个文件

创建新文件

“a+” (读写)

打开一个文件,在末尾进行读写

创建新文件

“rb+”(读写)

读写一个二进制文件

错误

“wb+” (读写)

创建一个读写的二进制文件

创建新文件

“ab+” (读写)

打开一个二进制文件,在末尾进行读写

创建新文件

例子:

int main()
{
	FILE* pf = fopen("data.txt", "w");   //通过指针FILE* pf找到并打开data.txt文件
	if (pf==NULL)  //判断pf是否指向NULL
	{
		perror("fopen");//perror可以把错误的信息打印到屏幕上
		return 1;  //在主函数中返回1表示不正常,返回0才正常
	}
	fclose(pf);//通过指针FILE* pf找到data.txt文件并关闭
	pf = NULL;//已经关闭了data.txt文件,创建的pf指针不能为空,否则就变为一个野指针
	return 0;
}

5.⽂件的顺序读写

输入流:磁盘中的文件数据源输入到内存里

输出流:内存中的内容存入硬盘中的文件里

以下函数默认读取与写入的内存地址是跟” .c”文件同一个目录下.

函数名

功能

适用于

fgetc

字符输入函数

所有输入流

fputc

字符输出函数

所有输出流

fgets

文本行输入函数

所有输入流

fputs

文本行输出函数

所有输出流

fscanf

格式化输入函数

所有输入流

fprintf

格式化输出函数

所有输出流

fread

二进制输入

所有输入流

fwrite

二进制输出

所有输出流

5.1、fgetc函数

int fgetc ( FILE * stream );

这个函数是用于打开文件并输出第一个字符,每输出一个字符光标会往前移动1

我们先创建一个文本文件.txt

与.c文件同一个目录创建一个data.txt文件,data.txt文件中写入abcdef这几个字符。

int main()
{
	//打开文件
	FILE* pf = fopen("data.txt", "r");
	if (pf==NULL)
	{
		perror("fopen");
		return 1;
	}
	//读取文件
	int ch = 0;      //因为文件存储的是ASCII码值,需要使用int类型的变量来接收ASCII码值
	ch = fgetc(pf);  //读取文件中一个字符,转换为ASCII码值,
	printf("%c\n", ch);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
} 

输出:

那么如何打印出整个data.txt文件中的字符。

int main()
{
	//打开文件
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	int ch = 0;

	//读取文件
	while ((ch = fgetc(pf)) != EOF)  //EOF的意思是当文件读取都末尾的空值时,会停止读取
	{
		printf("%c ", ch);
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

输出:

5.2、fputc函数

int fputc ( int character, FILE * stream );

这个函数可以把存储在内存上的内容写入到我们指定的文件当中

int main()
{
	//打开文件
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//输出字符
	fputc('a', pf);//把字符a写入data.txt文件中
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

成功写入data.txt文件

5.3、fputs函数

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

这个函数可以把字符串给保存到文本文件中

int main()
{
	//打开文件
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//输出字符
	fputs("hello world", pf);//把字符串写入data.txt文件中
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

5.4、fgets函数

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

这个函数是读取文本文件中字符,str表示字符串大小,num表示读取多少字符

//事先创建了一个data.txt文件,里面存入hello world字符串
int main()
{
	//打开文件
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//输入字符
	char str[12];        
	fgets(str, 12, pf);    //把data.txt文件中的内容前12个字符存入str中
		printf("%s", str);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

输出:

5.5、fprintf函数

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

这个函数的使用方法跟printf有相同之处,这个函数输出文本文件的里面的内容。

struct S
{
	char name[20];
	int age;
	float score;
};
int main()
{
	struct S s = { "lihua",18,97.3f };//结构体s中存放的内容
	//打开文件
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//输出
	fprintf(pf, "%s   %d     %f", s.name, s.age, s.score);//把结构体中的内容存放到pf中
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

成功保存

5.6、fscanf函数

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

这个函数可以读取文件中的内容,我们就使用上一个函数中存放在文本文件中的内容做示范

struct S
{
	char name[20];
	int age;
	float score;
};
int main()
{
	struct S s = { 0 };//结构体s中存放的内容
	//打开文件
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//输入
	fscanf(pf, "%s %d %f", s.name, &(s.age), &(s.score));  //字符串无需取地址,因为会自动取首地址,把data.txt文件中的内容取出来放入结构体s中
	printf("%s  %d  %f", s.name, s.age, s.score);//打印存放在结构上s中的内容
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

输出:

浮点数无法做到精确取值,所有有余数是很正常的事情。

5.7、fwrite函数

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

这个函数把内容输出到文本文件中,使用二进制保存

int main()
{
	//打开文件
	FILE* pf = fopen("data.txt", "wb");//wb是二进制输出
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//二进制输出
	int arr[5] = { 1,2,3,4,5 };
	fwrite(arr, sizeof(int), 5, pf);//把arr大小的文件,以int类型大小,保存5个字符/数字到pf中
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

可以看见,我们保存成功

5.8、fread函数

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

这个函数可以读取二进制文本文件,我们读取上一个函数创建的文本文件内容

int main()
{
	//打开文件
	FILE* pf = fopen("data.txt", "rb");//wb是二进制输出
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//二进制输出
	int arr[5] ;   //创建一个字符串
	fread(arr, sizeof(int), 5, pf);//读取pf指向的二进制文件,保存5个以int大小到arr数组中
	for (int i = 0; i < 5; i++)
	{
		printf("%d ", arr[i]);
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

输出:

5.9、 sprintf函数

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

这个函数是将数组,字符串,浮点型,变为一个字符串进行输出

struct S
{

	char name[20];
	int age;
	float score;
};

int main()
{
	char str[100];  //用于存储结构体s中的内容
	struct S s = { "xiaoming" ,18,95.5 };
	sprintf(str, "%s %d %f", s.name, s.age, s.score);  //把结构体s中的内容放入str中
	printf("%s ", str);
	return 0;
}

输出:

5.10、sscanf函数

int sscanf ( const char * s, const char * format, ...);

这个函数是将变量取出来,放入到另一个空变量中(类型与大小要相同)

struct S
{

	char name[20];
	int age;
	float score;
};

int main()
{
	char str[100];
	struct S s = { "xiaoming" ,18,95.5 };
	struct S tmp = { 0 };
	sprintf(str, "%s %d %f", s.name, s.age, s.score);  //把结构体s中的内容放入str中
	sscanf(str, "%s %d %f", tmp.name, &(tmp.age),&(tmp.score));   //把str中的内容放入tmp结构体中
	printf("%s %d %f", tmp.name, tmp.age, tmp.score);    //打印结构体tmp中从值
	return 0;
}

输出:

6. ⽂件的随机读写

上面讲了顺序读写,我们这一节来讲随机读写

6.1、fseek函数

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

这个函数是使用光标来定位的,并不是从初始位置开始

offset是我们需要移动几个光标,origin移动光标的时候想从什么地方开始移动,上图中已经写了,SEEK_SET光标在文件开头,SEEK_CUR文件指针的当前位置,SEEK_END光标在文件的末尾

6.1.1、SEEK_SET文件开头

//在data.txt中存入abcdef内容
int main()
{
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	int ch = 0;
	fseek(pf, 4, SEEK_SET);  //从起始位置开始便便宜4个光标
	ch = fgetc(pf);
	printf("%c", ch);
	fclose(pf);
	pf = NULL;
	return 0;
}

输出:

便宜四个位置就是e

6.1.2、SEEK_CUR文件指针的当前位置

//在data.txt中存入abcdef内容
int main()
{
	int ch = 0;
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	ch = fgetc(pf);           //fgetc运行完以后,光标会自动偏移一个位置
	fseek(pf, 4, SEEK_CUR);  //从当前位置开始偏移4个光标
	ch = fgetc(pf);
	printf("%c", ch);
	fclose(pf);
	pf = NULL;
	return 0;
}

输出:

6.1.3、SEEK_END文件结束 *

//在data.txt中存入abcdef内容
int main()
{
	int ch = 0;
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fseek(pf, -2, SEEK_END);     //光标从结尾开始运行,移动-2个位置
	ch = fgetc(pf);
	printf("%c", ch);
	fclose(pf);
	pf = NULL;
	return 0;
}

输出:

6.2、ftell函数

long int ftell ( FILE * stream );

这个函数是计算光标距离起始位置有多长

//在data.txt中存入abcdef内容
int main()
{
	int ch = 0;
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	fseek(pf, 4, SEEK_SET);     //从起始位置开始偏移4个光标
	ch = fgetc(pf);
	printf("%c\n", ch);
	printf("%d\n", ftell(pf));  //计算当前光标到起始位置的距离     
	fclose(pf);
	pf = NULL;
	return 0;
}

输出:

6.3 rewind函数

这个函数可以将光标恢复到起始位置

//在data.txt中存入abcdef内容
int main()
{
	int ch = 0;
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	fseek(pf, 4, SEEK_CUR);     //从当前位置开始偏移4个光标
	ch = fgetc(pf);
	printf("%c\n", ch);
	printf("%d\n", ftell(pf));
	rewind(pf);                 //光标恢复到起始位置
	printf("%d\n", ftell(pf));
	fclose(pf);
	pf = NULL;
	return 0;
}

输出:

7. ⽂件读取结束的判定

1、文本文件读取

我们可以使用feof这个函数来检查文件是否被正常读取

fgetc是否为EOF

fgets是否为NULL

如果是的话,那么文件读取没用文件,否则就有问题

2、二进制文件读取

二进制读取结束判断,判断返回值是否小于实际要读的数

7.1、字符串文本例子

//在data.txt中存入abcdef内容
int main()
{
	int ch = 0;
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	while ((ch = fgetc(pf)) != EOF) // 标准C I/O读取⽂件循环
	{
		putchar(ch );//putchar也是打印的意思,这个是打印变量
	}
	//判断是什么原因结束的
	if (ferror(pf))
		puts("\nI/O error when reading");   //puts是打印的意思
	else if (feof(pf))
		puts("\nEnd of file reached successfully");
	fclose(pf);
	pf = NULL;
	return 0;
}

7.2、二进制文本例子

enum { SIZE = 5 };
int main(void)
{
	double a[SIZE] = { 1.,2.,3.,4.,5. };
	FILE* fp = fopen("test.bin", "wb"); // 必须⽤⼆进制模式
	fwrite(a, sizeof * a, SIZE, fp); // 写 double 的数组
	fclose(fp);
	double b[SIZE];
	fp = fopen("test.bin", "rb");
	size_t ret_code = fread(b, sizeof * b, SIZE, fp); // 读 double 的数组
	if (ret_code == SIZE) {
		puts("Array read successfully, contents: ");
		for (int n = 0; n < SIZE; ++n)
			printf("%f ", b[n]);
		putchar('\n');
	}
	else { // error handling
		if (feof(fp))
			printf("Error reading test.bin: unexpected end of file\n");
		else if (ferror(fp)) {
			perror("Error reading test.bin");
		}
	}
	fclose(fp);
}

8.、⽂件缓冲区

文件缓冲区的意思我们可以理解为,在内存中存放的数据,存放到一定数量的时候,再写入硬盘里,如果没有文件缓冲区那么就需要时时刻刻把文件数据放入到硬盘里,这样太浪费算力了。

评论 40
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值