10.1 对文件的有关的基本知识
10.1.1 什么是文件
一、背景知识
C语言编写的程序是源程序,计算机不能直接识别和执行高级语言所写的指令,必须用编译器把C源程序翻译成二进制形式的目标程序,然后再将目标程序与系统的函数库以及其他目标程序连接起来,形成可执行程序。
将一个C程序编写好之后,要在计算机上运行,必须经历以下几个阶段:
准备阶段(文件名约定)
不同的环境具有不同的命名约定,这里以Windows系统为例
(1)C源代码的以“.c”为后缀,源文件保存程序的定义,也称为定义文件;
(2)有#include指令包含到C源代码的文件称为头文件,用来保存文件的声明,以”.h”为后缀;
(3)C源程序编译后形成的文件称为目标文件,以”.obj”为后缀;
(4)将目标文件进行连接处理后得到的文件称为可执行文件,以”.exe”为后缀。
一、预编译(预处理):(.c/.cpp)
预编译过程主要处理那些源代码文件中的以“#”开始的预处理命令。比如#include
1.处理“#include”预编译指令,将被包含的文件插入到该预编译指令的位置。注意,这个过程是递归进行的,也就是说被包含的文件可能还包含其他文件。
2.删除掉所有注释"//“和”/* */".
…
二、编译(.i)
编译过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析、及优化后生产相应的汇编代码文件,这个过程往往是我们所说的整个程序构建的核心部分,也是最复杂的部分之一。也就是查错。主要错误有:语法错误,函数使用错误(只检查函数有没有声明,链接检查函数有没有实现的部分)。
三、汇编(.asm)
汇编器是将汇编代码转变成机器可以执行的指令,每一个汇编语句几乎都对应一条机器指令。汇编器的汇编过程相对于编译器来讲比较简单,他没有复杂的语法,也没有语义,也不需要做指令优化,只是根据汇编指令和机器指令的对照表一一翻译就可以了。
四、链接(obj.)
将编译后得到的所有的目标文件连接装配起来,再与函数库相连接成一个整体,生成一个可供计算机执行的可执行程序(.exe)。
文件主要可以分类为两种,一种称之为程序文件,还有一种称为数据文件;前面我们在第一章节介绍了代码是如何编译运行的,里面我们介绍了各种.c.exe文件。这个文件的内容是程序代码的叫做程序文件。
———————————————————————————————————
分割线
———————————————————————————————————
但是对于一个完整的程序,还有一个关键的东西,那就是数据。数据文件也属于文件的一部分,但不是程序文件。数据文件是来储存数据的。
咱们快来看看数据文件
什么是文件?我更想把文件理解只是一种数据流通的形式或者媒介。他不是数据本身。
文件与数据:举个例子,如果说沙漠中所有的沙子是漫漫的数据,那么说如果说你想把这些沙子运到别处你只能通过某种容纳物件,比如沙袋来运送。而这里所说的沙袋就是咱们说的文件。而沙子的往来运送,可以理解为是数据得输入输出。可以称之为数据流。数据的流动。
输入输出(Input output,IO)是指程序(内存)与外部设备(键盘、显示器、磁盘、其他计算机等)进行交互的操作。几乎所有的程序都有输入与输出操作,如从键盘上读取数据,从本地或网络上的文件读取数据或写入数据等。通过输入和输出操作可以从外界接收信息,或者是把信息传递给外界。
数据在磁盘或者其他外部设备和内存之间传递的过程叫做文件流,类似水从一个地方流动到另一个地方。数据从外部设备到内存的过程叫做输入流,从内存到外部设备的过程叫做输出流。
注意:所有保存在磁盘的文件都要载入内存才能处理,所有的数据必须写入磁盘中的文件才不会丢失。
咱们宏观以为数据就是一段段代码,其实这一段段代码都是有一个个字符组成得无行数限制得一行,就像一条蜿蜒不绝得河流,这条河流流动得方向,开始结束,只受到程序的控制。
但是咱们正常范围理解的文件,也即是广义上理解为,书上说:所有储存在外部介质中数据的集合都可以叫做文件。这相当于沙子加上沙包两者的综合。我们称之为一个文件。
10.1.2 文件名
文件名包括三部分;
D:\CC\temp\file1.dat
其中包括文件路径,文件名主干,还有文件后缀。这是一个完整的文件名。我们生活里说的文件名通常是这里的文件名主干。但是你要知道真正的文件名应该是什么。
10.1.3文件的分类
存储器包括内部存储器(内存)、外部存储器(外存)、寄存器。 内存包括只读存储器(ROM,Read Only memory)(只读,断电后数据保留)、随机存取存储器(RAM,Random Access Memory)(主存)(内存条)(可读可写,断电后数据丢失)、高速缓冲存储器(CACHE)。 外存包括磁盘、光盘、U盘等。 磁盘分硬盘和软盘。软盘容量小,在光盘时代被淘汰。光盘写入数据需光盘刻录机,在U盘时代被淘汰。 硬盘分为机械硬盘、固态硬盘、混合硬盘。 寄存器是CPU(Central Processing Unit)的组成部分。 从功能方面来看,CPU内部由寄存器,控制器,运算器和时钟四部分构成,各部分之间由总线上的电流信号相互连通。总线分数据总线、控制总线、状态总线三种。
从组织形式看,文件可以分为ASCALL文件,还有二进制文件。ASCALL文件他的二进制是一个字节一个字节的排序。
文本文件(ASCALL文件) 例如10000; 00110001...00110000 00110000 00110000 00110000 1 0 0 0 0
而对于二进制文件:
映像文件(二进制文件) 文本文件 例如:10000 00000000000000000010011100010000
优缺点:
ASCALL文件: 优点:一个字节代表一个字符,便于对字符进行处理,也便于输出字符。 缺点:占内存多,花费转换时间(电脑最终只识别二进制代码,需要转化为二进制文件) 二进制文件: 节省空间 时间;二进制文件使用的时候原封不动的输入到内存上去,不需要转化,不想ASCALl文件需要转化成二进制才能被处理。这样说用二进制比较方便。
10.1.4 文件缓冲区
C 采用“缓冲文件系统”处理数据。
就是说在内存中开辟一个缓冲区。包括输出缓冲区,输入缓冲区。还开辟了一个程序数据区。就是说如果想要把数据输入到数据区。需要先过渡一下。数据先一个一个存放到缓冲区,知道缓冲区存放满之后,一个个送到数据区。同样说输出,就是把数据一个个输出到缓冲区,然后就是在一次性输出到外存中。
缓冲区的个人理解
这里所说的缓冲区指的是为标准输入与标准输出设置的缓冲区,为什么要设置一个标准输入缓冲区主要是从效率上来考虑的,CPU处理数据,一般会在磁盘中或者缓冲区中去读取,相比于直接去磁盘去读写,在缓冲区读写会快很多,所以尽量把数据都放在缓冲区中,这样就可以是计算机不用区磁盘中读写,如果不设缓冲区会降低cpu的效率!
10.1.5 文件类型指针
每一个文件都会在内存中开辟一个文件信息区域,用来存放这个文件的某些信息。然后就可以通过访问存储在内存中的文件信息区域,找到存储在外部介质中的文件了。而文件信息区域的空间是通过结构体定义的。
typedef struct { short level; //缓冲区满和空的程度 unsigned flags;//文件状态标志 char fd;//文件描述符 unsigned char hold;//如缓冲区无内容不读取字符 short bsize;//缓冲区的大小 unsigned char *buffer;//数据缓冲区的位置 unsigned char *curp;//指针当前的指向 unsigned istemp;//临时文件的指示器 short token;//用于有效的检查 }FILE;
定义了一个结构体,这样内存中就开辟了一段储存空间,这段储存空间就充当文件信息区。
注意,这段储存空间实在stdio.h头文件中就定义好的。不需要要你去定义,而且变量名字规定是FILE。你可以直接使用就可以了;
说到这里,你坑定会问,怎么对这段结构体进行初始化,就是输入某一个文件的相关信息:这些信息是打开文件时由系统自动输入的,用户不必过问。
还有一个问题就是,如何访问这文件信息区域。
前面学过了指向结构体的指针。通过使用结构体的地址,其指向该结构体去访问它。不能直接通过变量去访问。
FILE *f1;
10.2 打开文件和关闭文件
读写文件时需要打开文件,读写完毕后需要关闭文件;
打开文件:建立相应得信息区域,文件缓冲区,定义一个指针变量指向它;
关闭文件:撤销文件信息区域,文件缓冲区,是这个指针变量不再指向;
10.2.1 用fopen函数打开数据文件
打开文件要用到打开文件的函数才能打开文件,该函数是在函数库中定义好的,你可以直接调用就可以了。调用方式如下:
fopen(文件名,使用文件方式)
FILE *fp; fp=fopen("a1","r"); 调用函数会有一个返回值,这个返回值就是具有该文件名的文件相对应的在内存中文件信息区域的首地址;
从上面可以看出,使用打开文件的函数,首先需要
a)打开文件的名字,准备访问的文件名
b)使用文件的方式是只读,还是只写,还是读写等
c)定义一个指针变量,把返回值赋值给该指针变量,这样指针变量就指向了该目标文件。
使用文件方式:
读:输入数据这个输入数据是从该文件输出,输入到程序
写:输出数据这个输出数据是从程序中输出数据,输入到文件
1)“r” 向一个已经存在,有数据的文件 ,从文件输出,输入数据到程序。如果指定文件不存在,那么显示出错。
2)“w” 程序输出数据,输入到文件,相当于文件写数据,如果没有该文件,则新建一个文件。如果有该文件,则删除该文件,然后新建一个新文件。
3)“a” 追加;在文件末尾添加数据,就是说程序输出数据,输入到文件,但是不删除原有数据,在原有数据的末尾接着写,这就是追加。如果文件不存在,显示出错。
4)“r+,w+,a+”就是既可以读又可以写,
首先是“r+”这个文件一般已经存在。它可以从文件输出数据,输入到程序中。输出完数据以后,也可以接受来自程序的数据。
然后是“w+”,是新建一个文件,这个文件既可以读,也可以写;
最后是“a+”,是程序输出数据,输入到文件,文件的数据可以不删除,直接到末尾处接着写。输入完了之后,可以拿去输出。
5)从上面可以看出,如果说读写出现错误;就是打开一个文件出现错误;可能是一下几个原因;
用r方式打开一个并不存在的文件; 磁盘出现故障; 磁盘已满,无法建立一个新的文件; 此时调用这个函数出现了错误,函数无法返回你一个文件的具体地址,他会返回你一个空指针NULL;(在stdio.h头文件中NULL已经被定义成0) if((fp=fopen("file 1","r"))==NULL) { printf("cannot open this file\n"); exit(0);//关闭所有文件,终止正在执行的程序 }
6)咱们光靠fopen打开文件还不行,数据还不能随意从文件和程序中间传输,如果想让数据能够传输,还需要能够传输的渠道。流系统自带三种流文件,他们不需要通过fopen打开,只要程序一运行,他们会自动打开。
他们分别称为:标准输入流,标准输出流,标准出错输出流…
也分别在头文件中指定了指针变量去指向他们:
文件是数据源的一种,最主要的作用是保存数据。在操作系统中,为了统一对各种硬件的操作,简化接口,不同的硬件设备也都被看成一个文件。对这些文件的操作,等同于对磁盘上普通文件的操作。例如,通常把显示器称为标准输出文件,printf就是向这个文件输出,把键盘称为标准输入文件,scanf就是从这个文件获取数据。