【文件操作(一)】

1.为什么使用文件?

#include <stdio.h>

int main()
{          
	int n = 0;
	scanf("%d", &n);
	printf("%d", n);

	return 0;
}

如图所示代码,数据只是存储在内存中,程序跑完了内存回收了,数据也就没了。如果要将数据进行持久化的保存,我们可以用文件。

2.什么是文件?

磁盘(硬盘)上的文件是文件。
但是在程序设计中,我们⼀般谈的文件有两种:程序文件、数据文件(从文件功能的角度来分类的)。

2.1.程序文件

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

2.2.数据文件

文件的内容不⼀定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。比如最常见的.txt后缀的。

2.3.文件名

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

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

根据数据的组织形式,数据文件被称为文本文件或者⼆进制文件。

数据在内存中以⼆进制的形式存储,如果不加转换的输出到外存的文件中,就是二进制文件
打开一个记事本,发现全是读不懂的乱码,那基本上就是二进制文件。

如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件
能够正常阅读的基本上是文本文件。

⼀个数据在文件中是怎么存储的呢?
字符⼀律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。
如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符⼀个字节),而二进制形式输出,则在磁盘上只占4个字节。(VS2019测试)
在这里插入图片描述

二进制存储

#include <stdio.h>
int main()
{
    int a = 10000;
    FILE* pf = fopen("test_1.txt", "wb");//打开文件
    fwrite(&a, 4, 1, pf);//写文件;二进制的形式写到文件中
    fclose(pf);
    pf = NULL;
    return 0;
}

结果:(是以十六进制展现)
在这里插入图片描述
在这里插入图片描述

4.文件的打开和关闭

以喝水为类比,读文件也是这么个流程。

1.打开水瓶2.打开文件
2.喝水2.读/写文件
3.关闭水瓶3.关闭文件

4.1. 流和标准流

4.1.1 流

在这里插入图片描述
我们程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输⼊输出
操作各不相同,为了方便程序员对各种设备进行方便的操作,我们抽象出了流的概念,我们可以把流
想象成流淌着字符的河。
C程序针对文件、画⾯、键盘等的数据输入输出操作都是通过流操作的。
⼀般情况下,我们要想向流里写数据,或者从流中读取数据,都是要打开流,然后操作。

4.1.2 标准流

那为什么我们从键盘输⼊数据,向屏幕上输出数据,并没有打开流呢?
那是因为C语眼程序在启动的时候,默认打开了3个流:
• stdin - 标准输入流,在大多数的环境中从键盘输入,scanf函数就是从标准输入流中读取数据。
• stdout - 标准输出流,大多数的环境中输出至显示器界⾯,printf函数就是将信息输出到标准输出
流中。
• stderr - 标准错误流,⼤多数环境中输出到显示器界面。
这是默认打开了这三个流,我们使⽤scanf、printf等函数就可以直接进行输⼊输出操作的。
stdin、stdout、stderr 三个流的类型是: FILE* ,通常称为文件指针。
C语言中,就是通过 FILE* 的文件指针来维护流的各种操作的。

4.2. 文件指针

缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”。
每个被使用的文件都在内存中开辟了⼀个相应的文件信息区,用来存放文件的相关信息(如文件的名
字,文件状态及文件当前的位置等)。这些信息是保存在⼀个结构体变量中的。该结构体类型是由系
统声明的,取名 FILE.

例如,VS2013 编译环境提供的 stdio.h 头文件中有以下的文件类型申明:

struct _iobuf {
	char *_ptr;
	int _cnt;
 	char *_base;
 	int _flag;
 	int _file;
 	int _charbuf;
 	int _bufsiz;
 	char *_tmpfname;
 };
typedef struct _iobuf FILE;

不同的C编译器的FILE类型包含的内容不完全相同,但是⼤同小异。
每当打开⼀个文件的时候,系统会根据文件的情况自动创建⼀个FILE结构的变量,并填充其中的信
息,使用者不必关心细节。
⼀般都是通过⼀个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。
下⾯我们可以创建⼀个FILE*的指针变量:

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

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

4.3.文件的打开和关闭

文件在读写之前应该先打开文件,在使⽤结束之后应该关闭文件。
在编写程序的时候,在打开文件的同时,都会返回⼀个FILE*的指针变量指向该文件,也相当于建立了
指针和文件的关系。
ANSI C 规定使⽤用fopen 函数来打开文件, fclose 来关闭文件。

//打开⽂件
FILE * fopen ( const char * filename, const char * mode );
//关闭⽂件
int fclose ( FILE * stream );

mode表示文件的打开模式,下面都是文件的打开模式:

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

PS:你分得清读与写,输入和输出吗?
在这里插入图片描述
读:从文件中读数据输入到程序中去
写:从程序中写数据输出到文件中去

以下是一个实例:

int main()
{
	FILE* pf = fopen("test_1.txt", "w");//如果有,先情况原文件再操作
	                                 //如果没有,则创建一个
	if (pf == NULL)//文件打开失败返回NULL
	{
		perror("fopen");
		return 1;
	}
	//读文件

	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

这个“w”操作在这里并无明显结果,主要了解代码结构和逻辑。

5.文件的顺序读写

5.1.函数归类

函数名功能适⽤于
fgetc字符输⼊函数所有输⼊流
fputc字符输出函数所有输出流
fgets⽂本⾏输⼊函数所有输⼊流
fputs⽂本⾏输出函数所有输出流
fscanf格式化输⼊函数所有输⼊流
fprintf格式化输出函数所有输出流
fread⼆进制输⼊⽂件
fwrite⼆进制输出⽂件

上面说的适用于所有输⼊流⼀般指适用于标准输⼊流和其他输⼊流(如文件输⼊流);所有输出流⼀般指适用于标准输出流和其他输出流(如文件输出流)。

5.2.示例

示例一:

#include<stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("test_1.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	fputc('a', pf);
	fputc('b', pf);
	fputc('c', pf);

	/*char ch = 0; //写26个字母的方法
	for (ch = 'a'; ch <= 'z'; ch++)
	{
		fputc(ch, pf);
	}*/

	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

在这里插入图片描述
示例二:(在示例一的基础上)

#include<stdio.h>
int main()
{
	//打开文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	int ch = fgetc(pf);
	printf("%c\n", ch);

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

	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;
	}
	//读文件
	int ch = 0;
	while ((ch = fgetc(pf)) != EOF)//如果还没读完
	{
		printf("%c", ch);
	}

	//关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

实例四:

int main()
{
	//1. 打开文件
	FILE*pf = fopen("test_1.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	fputs("hello world\n", pf);
	fputs("hello bit", pf);


	//2. 关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

在这里插入图片描述

实例五:

int main()
{
	//1. 打开文件
	FILE* pf = fopen("test_1.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	char arr[20] = { 0 };



	while (fgets(arr, 50, pf) != NULL)
	{
		printf("%s", arr);
	}

	//2. 关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

在这里插入图片描述

PS:比如fgets(arr, 10, pf),一次操作,如果数据足够,就只会读9个加一个‘\0’;
如果数据长度很长一次读不完比如hello worldhhhhhhhhhhhhh那么就用while多读几次,只要一行里没读完就不会读’\n’到下一行去。

6.结语

更多的关于文件操作的知识,请关注文件操作(二)

链接: 文件操作(二)

  • 24
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值