C语言深入文件输入输出


文件输入输出

掌握了标准输入输出后再学习文件的输入输出就简单多了,更重要的是之前我们除了能和控制台交互做不了更多的事情。文件的输入输出也是标准库包含的内容,也就是说输入输出是C语言最基本的功能,任何平台都支持。仅凭这两个功能C已经可以做很多事了,windows和linux很多命令都是C写的,像mysql这样的数据库也是用C写的,一些压缩工具和编码工具也是用C写的,然后以命令行方式提供,它们都没有界面,仅用了标准库,但功能却非常强大。

流(数据流)是一个假象的逻辑体,可以将任何流式传输的数据称为数据流,这有利于形象化数据传输。标准输入输出和文件的输入输出虽然作用不同,但思路是一样的,流通过通道从一端流向另外一端,必须先建立通道才能进行数据流的操作,标准输入输出通道在程序启动时已经自动建立,可以直接访问stdin和stdout,文件读写需要打开和关闭文件。数据流在硬件中的传输方式都是二进制的,但为了方便阅读和操作,C标准将数据读写分为文本和二进制,控制台仅以字符形式读写,因此标准输入输出流只处理文本流,文件类型分为文本文件和二进制文件,其实文本文件和二进制文件没有本质的区别,因为数据传递的最小单位是字节,一个字节大小范围为0~255,刚好和256个ascii码对应,因此以文本方式也可以打开二进制文件,只不过显示的是乱码,可以说二进制数据是给机器读的,文本是给人看的。

获取文件信息

在C中描述文件使用结构体FILE,FILE是操作文件打开和关闭的结构体,它包含文件打开关闭状态、缓冲区等诸多信息,包含如下属性:
_flag 读写状态标志
_bufsiz 缓冲区大小
_base 缓冲区起始地址
_cnt 缓冲区有效字节数
_ptr 读写指针
这些属性看起来和标准输入输出的结构体相同,确实如此,实际上C将硬件设备也看作文件,这符合linux系统思想,linux诞生于unix,unix和C语言是一个人发明的,这个牛人先发明了C语言,然后用C编写了unix,因此C标准和linux在很多概念上是统一的。标准输入输出被看作一种设备文件,这个设备文件只读写字符,只顺序读取不随机读取,因此它只是文件输入输出功能的一部分实现。File中的成员都是有关文件打开关闭和与读写缓存相关的属性,作用在于建立流传输通道,在内存中为文件开辟读写缓存,而文件自身的信息,包括大小、创建时间等信息并不在其中,这些信息保存在文件中,可以编写代码读取,读取文件信息并不需要打开文件,这些信息会放入另一个结构体stat中,获取方式如下:

#include <stdio.h>
#include <sys/stat.h>

int main()
{
   
	struct stat statbuf;
	int result;
	result=stat("abc.txt",&statbuf);
	printf("文件大小:%ld\n", statbuf.st_size);
	printf("所在磁盘:%c:\n", statbuf.st_dev+'A');
	printf("创建时间:%s\n", ctime(&statbuf.st_atime));
	return 0;
}

stat结构体是在stat.h中定义的,必须导入sys/stat.h才能使用,使用stat()函数传入文本路径和结构体指针后,文件信息会被写入到结构体stat中,不同的平台和编译器可能会有区别,但常用的几个属性是相同的,其中比较重要的是st_size属性,记住读取文件信息并不需要打开文件,这点很重要,很多教程介绍通过打开文件然后设置指针的方式来计算文件大小,当扫描大量文件时这种方式效率非常低。

文件的打开和关闭

前面讲过文件的输入输出需要手动打开和关闭文件,可以理解为通道的建立和断开,那么文件在打开和关闭时具体做了哪些事情呢?我们知道C程序启动时会在内存中建立静态存储区和动态存储区,这些都属于程序数据区。C还包含一个文件缓冲区,文件的读写不是从磁盘中一个个字节读取的,那样效率太低,而是通过缓冲区一批批的读写,只有当缓冲区满了后才读入程序区或写入磁盘。打开一个文件就是在内存中创建FILE结构体和为文件建立缓冲区,关闭文件是先将缓冲区的数据写入磁盘,然后删除FILE结构体,撤销文件缓冲区,如果文件只打开不关闭会造成信息残余和缓冲区内容不能写入磁盘,有些操作系统当程序关闭时会自动将缓冲区写入磁盘,但不要指望总操作系统帮你做这事,应该养成手动关闭的习惯。

打开文件使用fopen()方法,例如:
fopen(“abc.txt”, ”r”);
第一个参数是文件路径名,路径名可以是绝对路径或相对路径,使用IDE调试或打开项目时,相对路径可能是项目文件夹,例如VS将生成的程序放入Debug文件夹中,如果将abc.txt放到Debug中,相对路径为Debug/abc.txt。第二个参数是打开方式,打开方式有很多种,如下表:

打开方式 描述
“r” 以“只读”方式打开文件。只允许读取,不允许写入。文件必须存在,否则打开失败。
“w” 以“写入”方式打开文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么清空文件内容(相当于删除原文件,再创建一个新文件)。
“a” 以“追加”方式打开文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么将写入的数据追加到文件的末尾(文件原有的内容保留)。
“r+” 以“读写”方式打开文件。既可以读取也可以写入,也就是随意更新文件。文件必须存在,否则打开失败。
“w+” 以“写入/更新”方式打开文件。既可以读取也可以写入,也就是随意更新文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么清空文件内容(相当于删除原文件,再创建一个新文件)。
“a+” 以“追加/更新”方式打开文件。既可以读取也可以写入,也就是随意更新文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么将写入的数据追加到文件的末尾(文件原有的内容保留)。

从上面表格可以看出,以”r”方式打开的文件必须存在,”w”可以创建或覆盖文件,”a”用于追加内容,默认情况下打开方式为文本,如果以二进制方式打开需要在中间加一个”b”,例如:
rb 二进制只读
wb+ 二进制读写
ab 二进制追加
fopen()函数返回一个FILE类型的指针,它记录文件状态,也包含文件读写指针,内容读写都是通过这个指针操作的,如果文件打开失败则返回NULL值,我们通常会通过条件语句先检查文件是否打开成功,然后在进行下一步操作,例如:

#include <stdio.h>

int main()
{
   
	FILE *fp;
	if((fp=fopen("file1.txt","r"))!=NULL) puts("文件打开成功");
	fclose(fp);
}

由于VS将fopen()列为不安全的函数,这段代码在VS中会报错,要通过编译除了关闭SDL,还需要在预编译中添加一个宏_CRT_SECURE_NO_WARNINGS,如图:
在这里插入图片描述

关闭文件使用fclose(fp)方法,成功关闭返回0,不成功返回EOF或-1。文件关闭后会重置读写指针为起始位置,即使文件没有打开成功,调用fclose(fp)也不会导致程序错误,因此fclose()可以放到条件语句之外,通常也不需要测试返回值。

顺序读写文件

读写文件分为顺序读写和随机读写文件,顺序读写是一种简单的读写方式,它在读写字符或字节后读写指针自动后移,无需手动操作,这使得我们不必关心读写指针的位置。

读写字符

当内容为文本时,可以使用fgetc()和fputc()来读写字符,例如:

#include <stdio.h>

int main()
{
   
	FILE *fp;
	char c;
	//写入一个文件
	if((fp=fopen("file1.txt","w"))==NULL) puts("文件打开失败");
	else
	{
   
		fputc('a',fp);
		fputc('b',fp);
		fputc('c',fp);
		fclose(fp);
	}
	
	//读取刚才写入的文件
	if((fp=fopen("file1.txt","r"))==NULL) puts(
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值