#C语言——学习攻略:攻克 文件操作内容(一),根本不在话下!

🌟菜鸟主页:@晨非辰的主页

👀学习专栏:《C语言学习》

💪学习阶段:C语言方向初学者

名言欣赏:“人理解迭代,神理解递归。”


目录

1.  对文件的介绍

1.1  文件的作用

1.2  程序文件、数据文件

1.3  文件名

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

3.  文件的打开、关闭

3.1  流、标准流

3.2  文件指针

3.3  文件的打开、关闭

3.3.1  fopen函数

3.2.2  fclose函数

4.  文件的顺序读写(初)

4.1  fputc 函数

 4.2  fgetc 函数(一次性输入完数据,再回车输出)

4.3  feof、ferror 函数

4.4  fputs函数

4.5  fgets 函数


1.  对文件的介绍

1.1  文件的作用

--程序的数据存储在电脑的内存中,若程序退出,内存回收,数据会丢失,再次运行程序什么也没有。所以,数据存储在文件中,就可以持久保存。

--什么是文件?:磁盘(硬盘)上的文件就是文件。在程序设计中,文件一般分为两种:程序文件、数据文件(文件功能不同)。

1.2  程序文件、数据文件

  • 程序文件

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

  • 数据文件

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

--主要介绍数据文件,前面学习所处理数据的输入输出是以终端为对象,即从终端键盘输入数据,屏幕显示结果。

--有时会将信息输出到硬盘上,需要时再读取到内存中使用,这里处理的是磁盘上的文件。

1.3  文件名

--文件有自己的标识,便于进行识别、利用。

文件名包含3部分:文件路径+文件名主干+文件后缀

如:C:\code\test.txt

--C:\code\ ->文件路径;test ->文件名主干;.txt ->文件后缀;


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

--由数据的组织形式,数据文件被分为文本文件和二进制文件。

  • 二进制文件:数据在内存中以⼆进制形式存储,不加转换的输出到外存的文件中;
  • 文本文件:要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件;

(外存:硬盘、U盘...)

--数据在文件中怎样存储?

  • 字符以ASCII形式存储,数值型数据既可用ASCII形式存储,也可用二进制形式存储;
  • 整数10000,以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符⼀个字节),而二进制形式输出,则在磁盘上只占4个字节(int型);

--代码演示:(相关操作会在后面介绍)

int main()
{
	int a = 10000;
	FILE* pf = fopen("data.txt", "wb");//打开文件的操作
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fwrite(&a, 4, 1, pf);//将10000以二进制形式写到文件中

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

--想要观察二进制,在vs上打开:

        --运行结束会创建一个文件->

        --在vs上添加test文本文件、打开:


3.  文件的打开、关闭

3.1  流、标准流

  • 流:

--程序数据需要输出到外部设备,也要获取数据,不同外部设备的输入输出操作不同,为方便进行操作,抽象出了->"流"的概念,将”流“想成流淌字符的河。
--C语言对文件、画面、键盘等数据的输入输出操作都是由”流“完成的。一般,向”流“里写数据或读数据,都要打开”流“,在操作。

  • 标准流:

--为什么从键盘输入、屏幕输出,我们没有打开”流“?

        --C语言在程序启动时默认打开3个”流“:

  • stdin - 标准输入流:在大多数的环境中从键盘输入,scanf函数就是从标准输入流中读取数据;
  • stdout - 标准输出流:大多数的环境中输出至显示器界面,printf函数就是将信息输出到标准输出流中;
  • stderr - 标准错误流,大多数环境中输出到显示器界面;

--3个流的类型是 -> FILE * ,通常称为文件指针。 C语言中,就是通过 FILE* 的文件指针来维护流的各种操作的。

3.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;

--不同编译器FILE类型包含内容不完全相同,每次打开文件,系统会自动创建FILE结构的变量,并填充信息,不需要我们关心。

--一般是通过一个FILE指针来维护结构变量:

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

        --定义pf是⼀个指向FILE类型数据的指针变量。使pf指向某个文件的文件信息区(结构体变量)。通过该文件信息区中的信息能够访问文件。即—>通过文件指针变量能够间接找到与它关联的文件。

3.3  文件的打开、关闭

--对于文件,读写前要打开,使用结束要关闭:使用  fopen 函数打开文件, fclose 关闭文件。

        --编写程序的时,打开文件同时,会返回⼀个FILE*的指针变量指向该文件,相当于建立了指针和文件的关系。

        --头文件:<stdio.h>

3.3.1  fopen函数

FILE* fopen (const char* filename, const char* mode);

功能:

        用来打开参数 filename 所指定文件,同时将其和一个流进行关联,后续对流的操作是通关 fopen 函数返回的指针来维护的。具体对流的操作是通过参数mode指定的;

参数:

--filename:要打开的文件的路径和名称。可以是相对路径(如 "data.txt"),也可以是绝对路径(如 "C:\\Users\\data.txt");

(注意:在字符串中使用'\'时,需要用转义字符,写两个'\\'。)

--mode:文件的打开模式是一个字符串,指定文件是用于读、写、追加,以及是以文本模式还是二进制模式打开。这是 fopen 的核心参数,决定了能对文件进行何种操作;

返回值:一个 FILE* 类型(文件指针)

  • 文件打开成功,返回指向 FILE 结构的指针。可以用于后续操作中标识对应的流;

  • 文件打开失败,返回NULL,要对fopen的返回值进行判断,来验证文件是否成功打开;

--mode--文件操作方式:

文件使用方式含义若指定文件不存在
"r"(只读)为了输入数据,打开已经存在的文本文件出错
"w"(只写)为了输出数据,打开文本文件建立新的文件
“a”(追加)向文本文件尾添加数据建立新的文件
“rb”(只读)为了输入数据,打开二进制文件出错
"wb"(只写)为了输出数据,打开二进制文件建立新的文件
“ab”(追加)向二进制文件尾添加数据建立新的文件
“r+”(读写)为了读和写,打开文本文件出错
“w+”(读写)为了读和写 建立新的文件建立新的文件
“a+”(读写)打开文件,在文件尾进行读写建立新的文件
“rb+”(读写)为了读和写打开二进制文件出错

“wb+”(读

写)

为了读和写,新建新的二进制文件建立新的文件

“ab+”(读

写)

打开二进制文件,在文件尾进行读和写建立新的文件

--演示几种方式,体会函数使用:

  •    演示在当前目录下的操作
int main()
{
	//打开文件,"w"--不存在,创建文件
	FILE* pf = fopen("data.txt", "w");
	//这里的路径为相对路径,表示在当前工程目录下的data.txt文件

	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//后续读取文件操作
	//……

	//关闭文件
	//……

	return 0;
}

  •    演示在当前目录的上一级目中进行打开文件

./ -- 表示当前目录; ../ --表示当前目录的上一级目录

int main()
{
	//打开文件,"r"--不存在,出错
	FILE* pf = fopen("./../data.txt", "r");//只读打开,看是否存在

	//这里的路径为相对路径,表示在当前工程目录上一级目录下的data.txt文件
	//已经将文件剪切到上一级目录中

	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//后续读取文件操作
	//……

	//关闭文件
	//……

	return 0;
}

--结果正常运行
  •      演示在当前目录的上一级目录的上一级目录的操作
int main()
{
	//打开文件,"r"--不存在,出错
	FILE* pf = fopen("./../../data.txt", "r");//只读打开,看是否存在

	//这里的路径为相对路径,表示在当前工目录上一级目录的上一级目录下的data.txt文件
	//已经将文件剪切到相应目录中

	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//后续读取文件操作
	//……

	//关闭文件
	//……

	return 0;
}

--结果正常运行
  •      演示当前目录下上二级目录中某文件夹中文件夹中的操作
int main()
{
	//打开文件,"r"--不存在,出错
	FILE* pf = fopen("./../../test/test2/data.txt", "r");//只读打开,看是否存在

	//这里的路径为相对路径,表示在当前工目录上二级目录下的test文件下test2文件的data.txt文件
	//已经将文件剪切到相应目录中

	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//后续读取文件操作
	//……

	//关闭文件
	//……

	return 0;
}
  •    演示在桌面上的操作
int main()
{
	//打开文件,"r"--不存在,出错
	FILE* pf = fopen("C:\\Users\\Tian\\Desktop\\data.txt", "r");//只读打开,看是否存在

	//这里的路径为绝对路径,表示在桌面上的data.txt文件
	//已经将文件剪切到相应目录中

	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//后续读取文件操作
	//……

	//关闭文件
	//……

	return 0;
}

3.2.2  fclose函数

int fclose(FILE* stream);

功能:

        --关闭参数 stream 关联的文件,取消关联关系。与该流关联的所有内部缓冲区均会解除关联并刷新:任何未写入的输出缓冲区内容将被写入,任何未读取的输入缓冲区内容将被丢弃;

参数:

        --stream: 这是一个指向 FILE 对象的指针,该对象标识要被关闭的流。指针通常由fopen等函数成功调用后返回的;

返回值:成功--返回0、失败--返回EOF;

--注意:

--必须检查返回值;

--一个指针关一次:确保每个由 fopen 打开的 FILE* 指针都有且仅有一次对应的 fclose 调用。关闭一个已经关闭的指针或一个空指针会导致未定义行为(程序崩溃);

--作用域问题: 确保指针失效(如局部变量离开作用域)之前关闭文件。否则永远失去关闭机会,导致资源泄漏(类似于动态内存管理函数的释放、置空)。

--代码演示函数功能:

nt main()
{
	//打开文件,"w"--不存在,创建文件
	FILE* pf = fopen("data.txt", "w");
	//这里的路径为相对路径,表示在当前工程目录下的data.txt文件

	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	//后续读取文件操作
	//……

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

	return 0;
}

4.  文件的顺序读写(初)

--文件读写会涉及到以下函数:

函数名功能适用于
fgetc从输入流读取一个字符所有输入流
fputc向输出流写入一个字符所有输出流
fgets从输出流读取一个字符所有输入流
fputs向输出流写入一个字符所有输出流
fscanf从输入流读取带有格式的数据所有输入流
fprintf向输出流写入带有格式的数据据所有输出流
fread从输入流读取一块数据文件输入流
fwrite从输出流写入一块数据所有文件流

4.1  fputc 函数

int fputc(int characer, FILE* stream);

功能:将参数 character 指定的字符写入 stream 指向的输出流,通常用于向文件或标准输出流写入。写入字符之后,会调整指示器。字符会被写入流内部位置指示器当前指向的位置,随后该指示器自动向前移动⼀个位置;

参数:

--character: 要写入的字符。虽然参数类型是 int ,但函数内部会将其转换为 unsigned char 后写入;

--stream :  是⼀个FILE*类型的指针,指向了输出流(通常是文件流或stdout);

返回值:--成功: 返回写入的字符(以 int 形式返回);--失败/错误: 返回 EOF(End Of File,通常是 -1)。

int main()
{
	//打开文件
	FILE* ps = fopen("data.txt", "w");

	//判断返回值
	if (ps == NULL)
	{
		perror("fopen");
		return 1;
	}

	//写数据--文件流
	fputc('a', ps);
	fputc('b', ps);
	fputc('c', ps);

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

	return 0;
}


//循环输出一系列字符
int main()
{
	//打开文件
	FILE* ps = fopen("data.txt", "w");

	if (ps == NULL)
	{
		perror("fopen");
		return 1;
	}

	for (int i = 'a'; i <= 'z'; i++)
	{
		fputc(i, ps);
	}

	fclose(ps);
	ps = NULL;
	return 0;
}
int main()
{
	//写数据--标准输出流_屏幕
	fputc('a', stdout);
	fputc('b', stdout);
	fputc('c', stdout);
	return 0;
}

 --打开相应文件进行查看输出:

 4.2  fgetc 函数(一次性输入完数据,再回车输出

int fgetc(FILE* stream);

功能:从参数 stream 指向的流中读取一个字符。函数返回的是文件指示器当前指向的字符,读取这个字符之后,文件指示器自动前进道下⼀个字符;

参数:

        --stream: FILE* 类型的文件指针,可以是 stdin、其他输入流的指针。是 stdin 就从标准输入流读取数据。是文件流指针,就从文件读取数据;

返回值:

        --成功: 返回读取到的字符(以 unsigned char 转换后的 int 形式返回)。

        --失败/到达文件末尾: 返回 EOF(设置错误指示器( ferror )/文件结束指示器( feof ))。

int main()
{
	//打开文件
	FILE* ps = fopen("data.txt", "r");

	//检查返回值
	if (ps == NULL)
	{
		perror("fgets");
		return 1;
	}

    //在前面fputc函数操作的基础上,对文件内容进行读取
	//读取数据--文件流
	for (int i = 0; i < 10; i++)
	{
		//读取
		int c = fgetc(ps);
		//将读取的数据打印在屏幕
		fputc(c, stdout);
	}

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

输出:abcdefghij
int main()
{
	for (int i = 0; i < 4;i++)
	{
		//读数据--标准输入流
		int c = fgetc(stdin);
		fputc(c, stdout);
	}
	return 0;
}

--这里建议一次性输入完数据后,再回车输出!

4.3  feof、ferror 函数

int feof(FILE *stream);   // 检查是否到达文件末尾
int ferror(FILE *stream); // 检查是否发生文件错误
  • 在读取文件过程中,遇到文件末尾,文件读取结束。读取函数会在对应的流上设置文件结束指示符,指示符可以通过 feof 函数检测。如果 feof 函数检测到指示符已经被设置,则返回非0的值,没有设置则返回0;
  • 在读/写文件的过程中,发生了读/写错误,文件读取结束。读/写函数会在对应的流上设置错误指示符,错误指示符可以通过 ferror 函数检测到。如果 ferror 函数检测到错误指示符已被设置,则返回非0的值,没有设置则返回0;
--测试feof函数
int main()
{
	FILE* ps = fopen("data.txt", "r");
	if (ps == NULL)
	{
		perror("fopen");
		return 1;
	}

	//读文件-文件:abcdef
	int i = 0;
	for (i = 0;i < 10;i++)
	{
		int c = fgetc(ps);
		if (c == EOF)
		{
			if (feof(ps))
			{
				printf("遇到文件末尾了\n");//到f就没有了
			}
			else if (ferror(ps))
			{
				printf("读取发生错误\n");
			}
		}
		else
		{
			fputc(c, stdout);
		}
	}
	fclose(ps);
	ps = NULL;
	return 0;
}

4.4  fputs函数

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

功能:将参数 str 指向的字符串写入到参数 stream 指定的流中(不含结尾空字符 \0 ),用于文件流或标准输出(stdout);
参数:

        --str : 指针,指向要写入的字符串(必须以 \0 结尾);

        --stream : FILE* 的指针,指向要写入字符串的流;

返回值:--成功,返回非负整数;--失败,返回 EOF设置流的错误指示器,用 ferror() 检查原因;

int main()
{
	//打开文件
	FILE* ps = fopen("data.txt", "w");

	//判断
	if (ps == NULL)
	{
		perror("fopen");
		return 1;
	}

	//写数据
	fputs("abcd", ps);

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

4.5  fgets 函数

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

功能:从 stream 指定输入流中读取字符串,读取到换行符、文件末尾(EOF)或到指定字符数(含结尾空字符 \0 ),然后将读取到的字符串存储到str指向的空间;

参数:

        --str :指向字符数组的指针,指向的空间存储读取的字符串。

        --num :最大读取字符数(包含结尾的 \0 ,实际最多读取 num-1 个字符)。

        --stream :输入流的文件指针(文件流或 stdin )。

返回值:

        --成功,返回 str 指针;

        --在读取字符时遇到文件末尾,设置文件结束指示器,返回 NULL ,通过 feof()检测;

        --发生读取错误,设置流错误指示器,返回 NULL,通过 ferror() 检测。

int main()
{
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	char arr[20] = "xxxxxxxxxxxxxxxx";
	fgets(arr, 10, pf);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}


相关系列回顾:

#C语言——学习攻略:攻克 动态内存分配、柔性数组,根本不在话下!

 结语:本篇文章到此结束,对于C语言相关知识大家要多次回顾,如果这篇文章对你的学习有帮助的话,欢迎一起讨论学习,你这么帅、这么美给个三连吧~~~ 

评论 53
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值