【文件操作相关知识】

1.什么是文件

如果将数据持续化保存在电脑中,就要使用文件。

2.文件分类

在程序设计中,我们一般把文件分为两类:程序文件和数据文件

2.1程序文件

程序文件包括程序源文件(后缀是.c)、目标文件(windows环境后缀是.obj)、可执行程序(windows环境后缀为.exe)。

2.2数据文件

文件内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。

接下来主要讨论数据文件。

2.3文件名

文件名包含三部分,文件路径+文件名主干+文件后缀

例如:c:\code\test.txt

 3.二进制文件和文本文件

根据数据文件的存储类型,分为二进制文件文本文件

数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件

如果要求在外存上以ASCII码的形式存储,则需要在存储器前转换。以ASCII字符的形式存储的文件就是文本文件

那么,一个数据在文件中怎么存储呢?
字符一律以ASCII码的形式存储,数值型数据既可以用ASCII码的形式存储,也可以用二进制文件存储。

比如数字10000在文件中的储存。

 在VS上打开二进制文件的方法。

测试代码:

#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;
}//只是一个例子,下面会详细介绍

 

 

图中显示的就是10000的二进制存储形式(小端存储)。 

4.文件的打开和关闭

4.1流和标准流

4.1.1流

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

所以一般想要输入输出,都要先打开流。

4.1.2标准流

我们在敲代码的时候,可以直接scanf输入数据,printf输出数据,这是因为c语言程序中默认打开三个流:

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

stdout:标准输出流,在大多数环境中输出至显示屏,printf就是将信息输出到标准输出流中。

stderr:标准错误流,在大多数环境中输出至显示屏。

这三个流的类型是:*FILE,文件指针。

4.2文件指针 

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

每个被使用的文件在内存中会开辟相应的文件信息区,在这个信息区内会存放文件的相关信息(如文件名字,文件状态和文件当前所在位置等),这些信息会存放在结构体变量中。该结构体类型是由系统声明的,命名为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* pfile;//文件指针变量

定义pfile是指向FILE数据类型的指针变量,pfile可以指向某个文件的文件信息区,通过该文件信息区就可以访问该文件。

4.3文件的打开和关闭 

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

ANSIC规定用fopen打开文件,用fclose关闭文件。

mode表示文件的使用方式。

直白来讲,“读”是输入操作,“写”是输出操作

测试代码:

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");//可以显示错误信息
		return 1;
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

 

此时,在该路径下就会出现test.txt文件。

5.文件的顺序读写 

函数名称含义功能
fgets字符输入函数
所有输入流(包括stdin)
fputs字符输出函数所有输出流(包括stdout)
fgetc文本行输入函数所有输入流(包括stdin)
fputc文本行输出函数所有输出流(包括stdout)
fscanf格式化输入函数所有输入流(包括stdin)
fprintf格式化输出函数所有输出流(包括stdout)
fread二进制输入文件
fwrite二进制输出文件

5.1fputs函数和fgets函数

注意:fgets函数读取 (num-1) 个字符。

测试代码:

int main()
{
	//打开文件
	FILE* pf = fopen("data1.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fputs("abcdef", pf);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

此时,用记事本打开该文件,会出现如下结果:

测试代码:

int main()
{
	//打开文件
	FILE* pf = fopen("data1.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读取
	char arr[5] = "xxx";
	fgets(arr, 3, pf);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

其实,只读取了文件中(3-1)个字符,前两个字符是ab,第三个字符是‘\0’。

5.2fgetc函数和fputc函数

character在这里是字符的意思。

测试代码:

int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	for (int i = 0; i < 26; i++)
	{
		//fputc('a' + i, pf);
		fputc('a' + i, stdout);//显示到屏幕上
	}
	fclose(pf);
	pf = NULL;
	return 0;
}

测试代码:

int main()
{
	//打开文件
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	int ret = fgetc(pf);
	printf("%d\n", ret);//以ASCII的形式输出,没有用循环,只读取第一个字符
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

5.3fscanf函数和fprintf函数 

对比一下,scanf和printf函数

其实就是在scanf和printf的基础上多了一个参数,剩下的就是scanf和printf的基本使用方法

 测试代码:

struct Var
{
	char name[20];
	int age;
    float score;
};
int main()
{
	struct Var v = { "yangweimu",20,80.5f };
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror(pf);
		return 1;
	}
	fprintf(pf, "%s %d\n", v.name, v.age, v.score);
	fclose(pf);
	pf = NULL;
	return 0;
}

在该路径下,文件就会显示如下:

测试代码:

将文件中的数据读取出来

struct Var
{
	char name[20];
	int age;
	float score;
};
int main()
{
	struct Var v = {0};
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		return 1;
	}
	//写读文件
	fscanf(pf, "%s %d %f", v.name, &(v.age), &(v.score));
//name是一个数组,数组名本来就是地址,所以不用加&
	fprintf(stdout, "%s %d %.1f\n", v.name, v.age, v.score);
	//在屏幕上输出
	fclose(pf);
	pf = NULL;

	return 0;
}

5.4fread函数和fwrite函数

struct Stu
{
	char name[20];
	int age;
	float score;
};
	
int main()
{
	struct Stu s = {"zhangsan", 20, 90.5};
	
	FILE* pf = fopen("data.txt", "wb");
	if (pf == NULL)
	{
		return 1;
	}
	//二进制的形式写文件
	fwrite(&s, sizeof(s), 1, pf);
		
	fclose(pf);
	pf = NULL;
	
	return 0;
}

在该路径下会出现如下结果,因为fwrite函数是二进制输出,所以以文本文件打开有些字符无法识别。 

 

测试代码:

struct Stu
{
	char name[20];
	int age;
	float score;
};

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

	FILE* pf = fopen("data.txt", "rb");
	if (pf == NULL)
	{
		return 1;
	}
	//二进制的形式读文件
	fread(&s, sizeof(s), 1, pf);

	printf("%s %d %.1f\n", s.name, s.age, s.score);

	fclose(pf);
	pf = NULL;

	return 0;
}

 

 6.文件的随机读写

6.1fseek

通过文件指针的位置和偏移量来定位文件指针。

 

测试代码:

//test.txt文件中存放的是26个英文字母
int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fseek(pf, 0, SEEK_SET);
	int ch = fgetc(pf);
	printf("%c\n", ch);
	fclose(pf);
	pf = NULL;
	return 0;
}

因为用的是SEEK_SET,所以是文件开始,移动是0个,所以打印第一个字符。 (其实就是光标的移动,肯定是打印光标后面的字符。)

 

其他情况

测试代码:

int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//fseek(pf, 0, SEEK_SET);
	
	//printf("%c\n", ch);
	fseek(pf, -2, SEEK_END);
	int ch = fgetc(pf);
	printf("%c\n", ch);
	
	fclose(pf);
	pf = NULL;
	return 0;
}

 

 测试代码:

int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//fseek(pf, 0, SEEK_SET);
	
	//printf("%c\n", ch);
	fseek(pf, -2, SEEK_END);//y
	fseek(pf, -3, SEEK_CUR);//v
	int ch = fgetc(pf);
	printf("%c\n", ch);
	
	fclose(pf);
	pf = NULL;
	return 0;
}

6.2ftell函数 

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

 测试代码:

int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//fseek(pf, 0, SEEK_SET);
	
	//printf("%c\n", ch);
	fseek(pf, -2, SEEK_END);
	fseek(pf, -3, SEEK_CUR);
	int ch = fgetc(pf);
	int n = ftell(pf);
	printf("%c\n", ch);
	printf("%d\n", n);
	
	fclose(pf);
	pf = NULL;
	return 0;
}

7.文件读取结束的判定 

feof函数的作用:当文件读取结束的时候,判定是否是遇到文件尾结束

注意!!不能用feof函数判定文件结束!只是检查作用!!

例如:fgets 判断返回值是否是NULL

           fgetc判断是否是EOF

测试代码:

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
    int c;
    FILE* fp = fopen("test.txt", "r");
    if (fp == NULL)
    {
        perror("fopen");
        return 1;
    }
    //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);
    return 0;
}

 over~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值