【CC++语言入门篇】-- 文件操作
最近实在是太忙了,这篇整整就推迟了1个月了,实在是对不起。之前本打算这个模块就结束了,文件操作就不写了,但是文件操作又是一个很重要的东西,而且也刚好能够总结之前我们学习的所有知识。同时也为了将文件操作这个初学者认为很神秘的东西给本质化。因此,本篇将逐一介绍C语言的文件操作。(本模块的命名本来是想CC++一块儿讲解的,但是由于工作、毕业论文、业余时间的充电、还有要完成那个未知的梦等,因此因为时间问题C++就只能放在以后有机会再写了,因此本篇将是本模块的最后一篇,之后将不会再连载了,请大家谅解。)
好了,回到正题,先来看文件操作中的文件。所谓文件(file)一般指存储在外部介质上数据的集合,比如我们经常使用的mp3、mp4、txt、bmp、jpg、exe、rmvb等等。这些文件各有各的用途,我们通常将它们存放在磁盘或者可移动盘等介质中。那么,为什么这里面又有这么多种格式的文件呢?原因很简单,它们各有各的用途,区分就在于这些文件里面存放的数据集合所遵循的存储规则不一样。举个例子比如bmp图片文件,为什么他能够表示一张图片,因为它有固定的格式,哪一段到哪一段,哪个偏移到哪个偏移应该存放什么数据是规定好了的。比如有文件头,一般是一个结构体,存放的文件的一些信息,如图片的大小,像素等等。再后来有数据区。然后我们要显示一张图片,就只需要按照前面所说的规则将文件头结构和数据块读出来,然后将这些数据在屏幕上用颜色表示出来,就成了一张图片。其它文件格式也类似。
这里要说一个更重要的例子,对我们理解文件有好处。那么这个文件就是exe文件(这里只讨论windows平台),通常我们认为它是一个可执行程序,这无疑是增加了它的神秘度。从本质上来讲exe无非是一种固定的文件格式罢了。既然这样,它就有一套自己的存储规则。跟前面的图片文件一样有规则。此时,你可能会问:你这么说那我就可以纯手工(直接填写数据填充文件)写出一个exe可执行文件了? 面对你这个问题,我只能说你已经习惯思考了,已经习惯给自己提问了,已经很聪明了。那么答案是肯定的,你完全可以用一个编辑器直接填写数据写出一个helloworld.exe文件或者helloworld.dll文件。因为这些具有一定格式规则的文件一般是二进制存储的,于是我们可以用一个二进制编辑器新建一个二进制文件,然后向里面填写数据。然后双击运行输出“helloworld”字符串。你可能会觉得很有成就感,我之前就写过一个exe和dll。这里exe和dll的文件格式也就是著名的PE文件格式。有兴趣你可以去查阅相关资料,此非本文重点。
总结上面的认识,文件无非就是一段数据的集合,这些数据可以是有规则的集合,也可以是无序的集合。操作系统也就是以文件为单位对数据进行管理的。也就是说,要访问外部介质上的数据,必须先按照文件名进行查找,然后从该文件中读取数据。要想写数据到外部介质,必须得建立一个文件,然后再写入。因此,这样来看,你眼前的文件将是一堆一堆数据而已,也没有什么类型文件之分了,类型只是为了区分而已,假如你把一个exe文件的扩展名改为txt,把它用记事本打开,同样是可行的,只是会执行exe文件里面的东西而已。(这里又不得不提到一点,如果你是一名程序员或者爱好者,那么你不应该将你的文件扩展名给隐藏了,要让它显示出来,如果你隐藏了,无非是增加了它的神秘感,同时在文件操作上不方便。通过上面的本质,我相信你能体会到我为什么这么说。)
说到这里,你应该知道文件是什么了,那么再来看二进制文件和ASCII文本文件,为什么要分为这两种呢?
首先、文本文件方式存储多用于我们需要明显知道文件里面的内容时,比如ini、h、c等文件都是文本文件,这种文件存储的是字符(ASCII码),比如一个整数10000,类型是short,占2字节,存储文本形式将占用5个字节,一共5个字符。你可以想想更多的例子,体会文本文件方便之处(提示:这里的文本文件不是说是txt文件,而是指所有以文本格式存储的文件。)
其次、二进制文件方式多用于直接将内存里面的数据形式原封不动存放到文件里,比如上面的short 10000,在内存中占2字节,存储内容为10000的二进制数,存到文件后还是占2字节,内容也是10000的二进制。这种方式可以整块数据一块儿存储,同时还可以将内存数据映射到文件里。
由上面两点,C语言操作文件可以是字节流或者二进制流。它把数据看成是一连串字符(字节),而不需要考虑边界。C语言对文件的存取是以字节为单位的。输入输出的数据流的开始和结束仅受程序控制而不受物理符号(如回车换行符)控制。这种文件通常称为流式文件,大大增加了灵活性。我们可以产生很多自己的文件格式,在游戏程序里面,用得比较多的就是资源包的格式,一般就是自定义的存取规则。我之前也写了一个包文件,存取只需要遵循规则,原理是非常简单的。大家可以试试在脑子里面构造一个包文件。
在ANSI C标准中,使用的是“缓冲文件系统”。所谓缓冲文件系统指系统自动地在内存区为每一个正在使用的文件名开辟一个缓冲区,从内存向磁盘输出数据必须先送到内存中的缓冲区,装满后再一起送到磁盘去。反向也是如此。这里需要说明两个词:“输入”“输出”。输入表示从文件里读数据到程序里,输出表示从程序里写数据到文件中。
了解了文件及文件存储形式,下面该正式进入文件的读写了,不要太激动,还是慢慢来。细节往往决定成败。在缓冲文件系统中,有一个很重要的一个东西就是文件指针,每个使用的文件都会在内存中开辟一个区,用于存放文件的有关信息,这些文件信息就保存在一个结构体变量中的,这个结构体是由系统定义的,名为FILE,先来看看VC2005在stdio.h下FILE结构体的定义:
struct _iobuf
{
char *_ptr; // 指向buffer中第一个未读的字节int _cnt; // 记录剩余未读字节的个数
char *_base; // 指向一个字符数组,即这个文件的缓冲
int _flag; // FILE结构所代表的打开文件的一些属性
int _file; // 用于获取文件描述,可以使用fileno函数获得此文件的句柄。
int _charbuf; // 单字节的缓冲,即缓冲大小仅为1个字节,如果为单字节缓冲,_base将无效
int _bufsiz; // 记录这个缓冲的大小
char *_tmpfname; // temporary file (i.e., one created by tmpfile()
// call). delete, if necessary (dont have to on
// Windows NT because it was done by the system when
// the handle was closed). also, free up the heap
// block holding the pathname.
};
typedef struct _iobuf FILE;好了,上面的结构体就是这样定义的。这里不得不再次提到缓冲:
缓冲模式
常量(mode)
备注
无缓冲模式
_IONBF
该文件不使用任何缓冲,也可以说是字节缓冲只能保存一个字节。
行缓冲模式
_IOLBF
仅对文本模式打开的文件有效,所谓行,即是指每收到一个换行符( 或 ),就将缓冲flush掉
全缓冲模式
_IOFBF
仅当缓冲满时才进行flush
上面结构体中的_flag就标记了缓冲的信息(我们关心这三个):#define _IOYOURBUF 0x0100 // 使用用户通过setbuf提供的buffer
#define _IOMYBUF 0x0008 //
补充:软件开发 , C++ ,