文件和文件操作

文件的定义

文件名

c盘,d盘上面的文件都是文件,文件名就是它的路径,如图

磁盘上的文件被定义为文件,文件(功能角度)主要分为两大类,一类是程序文件,另一类是数据文件。

程序文件

程序文件包括源程序文件,也就是后缀为.c的文件,目标文件(后缀为obj),可执行程序(后缀为exe)。(以上均为在Windows环境下)

数据文件

有些文件内存储的是数据,程序在执行时从中读取数据,数据是从磁盘上的文件提取到内存,或者输出内容到文件,最终输出到的是磁盘,这种文件都被称作数据文件。

本篇介绍数据文件

数据文件可以分为文本文件和二进制文件两类

文本文件

以ASCII码存储的文件就是文本文件。

二进制文件

以二进制存储在内存中,当被提取到外存时,仍然是二进制的输出形式的文件被称为二进制文件。

数据具体的存储方式:字符一律按ASCII码值存储,数值型既可以按ASCII码值存储,也可以按照二进制形式存储。

下面用一段代码来看一下存储形式。

#include<stdio.h>
int main()
{

	int a = 10000;
	FILE* pf = fopen("test.txt", "wb");
	fwrite(&a, 4, 1, pf);
	fclose(pf);
	pf = NULL;

	return 0;
}

运行成功的结果: 

然后查看,首先鼠标右键单击资源文件,然后找到现有项

再找到创建的test.txt文件

找到后右击选择打开方式

往下找一找找到二进制编辑器选择上然后点击确定

最终展现的是以二进制形式存储的10000的十六进制形式

文件的打开和关闭

流、标准流

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

我们在进行键盘上的输入输出时没有进行打开流或者关闭流的操作,实质上c语言程序在启动时就默认打开了三个流:stdin 标准输入流,stdout 标准输出流,stderr标准错误流,这使得程序能用键盘进行输入,屏幕进行输出,还有报错。

文件指针

c语言中,就是通过文件指针(FILE*)来进行对流的操控的,其全称为文件类型指针。

当我们在创建文件,给文件内部进行输入信息的时候,每个文件都对应在内存中开辟一块空间进行对文件信息的存储,这些信息都保存在一个结构体变量内,而这个结构体变量是系统声名的,叫做FILE。

创建文件指针变量

通过文件指针变量我们可以读取或者输出内内容到其对应的文件内。

文件的打开和关闭

ANSI C规定使用fopen来打开文件,用fclose来关闭文件。

fopen有两个参数,第一个是你要操作访问的文件指针,第二个参数是你要进行的操作方式,

操作方式如下

还包括a(在文件末尾添加)、rb(读二进制文件)、wb(写入二进制文件)、ab(在一个二进制文件末尾进行添加)等

实例:

#include<stdio.h>
int main()
{

	int a = 10000;
	FILE* pf = fopen("test.txt", "wb");
	if (pf != NULL)
	{
		fwrite(&a, 4, 1, pf);
	}
	fclose(pf);
	pf = NULL;

	return 0;
}

文件的顺序读写

顺序读写的一些函数:

fputc:

#include<stdio.h>
int main()
{
	FILE* fp = fopen("test.txt", "w");
	if (fp == NULL)
	{
		perror("fopen");
		return 1;
	}
	 //打开文件

	//写入文件
	char ch = 0;
	for (ch = 'a'; ch <= 'z'; ch++)
	{
		fputc(ch, fp);
	}

	 //关闭文件
	fclose(fp);
	fp = NULL;
	return 0;
}

 fgetc:

#include<stdio.h>
int main()
{
	FILE* fp = fopen("test.txt", "r");
	if (fp == NULL)
	{
		perror("fopen");
		return 1;
	}
	 //打开文件

		//读入文件

	int ch = 0;
	while ((ch = fgetc(fp)) != EOF)
	{
		printf("%c", ch);
	}
	 //关闭文件
	fclose(fp);
	fp = NULL;
	return 0;
}

fputs: 

#include<stdio.h>
int main()
{

	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fputs("hello world\n", pf);
	fputs("hello bit", pf);

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

fgets:

#include<stdio.h>
int main()
{

	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	char arr[10] = { 0 };
	fgets(arr, 10, pf);
	//换行之后不读 留一个位置给\0 函数自己写的\0
	fclose(pf);
	pf == NULL;
	return 0;
}

fprintf:

#include<stdio.h>
struct S
{
	char name[20];
	int age;
	float score;


};
int main()
{
	struct S s = { "zhangsan",20,65.5 };
	//存储到文件
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	fprintf(pf, "%s %d %f", s.name, s.age, s.score);
	//写文件 以文本的方式
	fclose(pf);
	pf = NULL;
	return 0;
}

fscanf:

#include<stdio.h>
struct S
{
	char name[20];
	int age;
	float score;


};
int main()
{
	struct S s = { 0 };
	//读文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	fscanf(pf,"%s %d %f", s.name, &(s.age), &(s.score));
	//写文件 以文本的方式

	//printf("%s %d %f", s.name, s.age, s.score);
	fprintf(stdout,"%s %d %f", s.name, s.age, s.score);

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

fwrite:

参数分别为数组名(数组地址)、数组元素大小、数组元素个数、文件指针

#include<stdio.h>
int main()
{
	int arr[4] = { 1,2,3,4 };
	FILE* pf = fopen("tstt.txt", "wb");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	int sz = sizeof(arr) / sizeof(arr[0]);
	fwrite(arr, sizeof(arr[0]), sz, pf);   //以二进制的形式写入

	fclose(pf);
	pf = NULL;


	return 0;
}

fread:

参数分别为数组名(数组地址)、数组元素大小、数组元素个数、文件指针

#include<stdio.h>
int main()
{
	int arr[4] = { 0 };
	FILE* pf = fopen("tstt.txt", "rb");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	int sz = sizeof(arr) / sizeof(arr[0]);
	fread(arr, sizeof(arr[0]), sz, pf);   //以二进制的形式读到arr数组里面

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


	return 0;
}

 文件的随机读写

fseek:

fseek有三个参数,第一个参数就是你要进行操作的文件指针,第二个参数就是对于第三个参数(起始位置)的偏移量,第三个参数就是设置起始位置,起始位置有三个:SEEK_SET 文件开头、SEEK_CUR 文件指针当前的位置、SEEK_END文件末尾。

ftell:

ftell函数用来判断文件指针相对于当前位置的偏移量,它有一个参数,就是你当前操作的文件的文件指针。 

rewind:

rewind函数的作用是将你当前正在操作的文件指针重置到起始位置。它的一个参数是你正在进行操作的文件。

 使用实例:

#include<stdio.h>
int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		return 1;
	}

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

	//fseek(pf, 4, SEEK_CUR);
	//fseek(pf, 5, SEEK_SET);
	fseek(pf, -4, SEEK_END);

	//printf("%d\n", ftell(pf));
	//计算指针偏移量

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

	rewind(pf);
	//指针回到起始位置
	printf("%d\n", ftell(pf));


	fclose(pf);
	pf = NULL;

	return 0;
}

 文件读取结束状态判定

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

文本文件

#include <stdio.h>
#include <stdlib.h>
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);
	}
	//判断是什么原因结束的
	printf("\n");
	if (ferror(fp))
		puts("I/O error when reading");
	else if (feof(fp))
		puts("End of file reached successfully");
	fclose(fp);
	fp = NULL;
}

其中的fgetc当作while循环的条件用来判断读取是否结束,下面有两个函数,判断读取结束的原因。下面会提到两个函数。

二进制文件

二进制文件是否读取结束,判断的依据就是返回值是否小于实际要读的个数。

#include <stdio.h>
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);
	fp = NULL;
}

ferror用来判断文件指针读取结束原因是否为发生错误。

feof用来判断文件指针读取结束原因是否为遇到了文件末尾。

注意:这两个函数都不能用来判断是否读取结束,只能在已知它们结束了,然后去判断结束的原因。 

文件缓冲区

ANSIC规定了一个文件缓冲系统用来处理数据文件,从内存向磁盘输出数据会先送到内存中的缓 冲区,装满缓冲区后才⼀起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘⽂件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小是由编译器决定的。

程序数据区 —————— 中间有一个(输出)(输入)缓冲区——————硬盘

文件缓冲区存在的意义:为了不一直调用系统保存文件,在文件缓冲区满的时候再调用系统,存到硬盘当中。

  • 32
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值