章节结构
- C文件的有关基本知识
- 什么是文件
- 文件名
- 文件的分类
- 文件缓冲区
- 文件类型指针
- 打开与关闭文件
- 用fopen函数打开数据文件
- 用fclose函数关闭数据文件
- 顺序读写数据文件
- 怎样向文件读写字符
- 怎样向文件读写一个字符串
- 用格式化的方式读写文本文件
- 用二进制方式向文件读写一组数据
- 随机读写数据文件
- 文件位置标记及其定位
- 文件位置标记
- 文件位置标记的定位
- 随机读写
- 文件位置标记及其定位
- 文件读写的出错检测
什么是文件
文件有不同的类型。在程序设计汇总,主要用到两种(程序文件、数据文件)
- 程序文件。包括源程序文件、目标程序文件、可执行文件。这种文件的内容是程序代码
- 数据文件。文件的内容是供程序运行时读写的数据。
当把数据输出到磁盘上保存,这样得到的文件就是磁盘文件
操作系统把各种设备都统一作为文件来处理。
文件(file)一般指存储在外部介质上数据的集合
输入输出是数据传送的过程,被称为流(stream),即数据流。流是一个传输通道,数据可以从运行环境流入程序中,也可以从程序流至运行环境。
C语言把文件看做一个字符(或字节)的序列,即由一个一个字符(或字节)的数据顺序组成。
一个输入输出流就是一个字符流或字节(内容为二进制数据)流。
C语言的数据文件由一连串的字符(或字节)组成,而不考虑行的界限,两行数据间不会自动加分隔符,对文件的存取是以字符(字节)为单位的。
输入输出数据流的开始和结束仅受程序控制而不受物理符号(如回车换行符)控制,这就增加了处理的灵活性。这种文件称为流式文件。
文件名
文件标识(为方便起见,又称为文件名)包括3个部分:文件路径;文件名主干;文件后缀。
(日常说的某某文件名称就是不加路径和后缀的文件名主干)
文件后缀:用来表示文件的性质。
文件的分类
根据数据的组织形式,数据文件分为ASCII文件和二进制文件
数据在内存中是以二进制形式存储的。而在外存中(如磁盘等)则分情况讨论。
字符在磁盘上一律以ASCII形式存储,数值型数据即可以用ASCII代码形式存储,也可以用二进制形式存储。
形式 | 数值 |
---|---|
整数 | 10000 |
内存中存储形式 | 00000000 0000000 00100111 00010000 |
ASCII形式 | 00110001 00110000 00110000 00110000 00110000 |
二进制形式 | 00000000 00000000 00100111 00010000 |
二进制形式和ASCII形式的区别在于:二进制形式是将数值当做一个整体来换算,而ASCII形式是将数值的每一位上的数字进行一一转换
根据《数字电路技术基础》的观点就是二进制形式是数制的一种,是表示大小的。而ASCII形式是码制的一种,仅仅是代号,用来标记不同事物的而已。
-
如果不加转换的输出到外存,就是二进制文件,
可以认为这就是存储在内存的数据的映像,也称之为映像文件 -
如果要求在外存上以ASCII代码形式存储,则需要在存储前进行转换。
ASCII文件又称文本文件,每一个字节都存放一个字符的ASCII代码。
用ASCII码形式输出时字节与字符一一对应,一个字节代表一个字符
文件缓冲区
ANSI C标准采用缓冲· 文件系统处理数据文件。
即系统自动的在内存区为程序中每一个正在使用的文件开辟一个文件缓冲区。
当缓冲区满了以后,再从缓冲区诸葛的讲数据送到程序数据区(给程序变量)
每个文件在内存中只有一个缓冲区, 在向文件输出数据时,它就作为输出缓冲区,在从文件输入数据时,它就作为输入缓冲区
文件类型指针
缓冲文件系统的关键概念文件类型指针,简称文件指针。
每个被使用的文件都在内存中开辟一个相应的文件信息区,用来存放文件的有关信息
如:文件的名字、文件状态及文件当前位置等
这些信息保存在一个结构体变量中的。
该结构体类型是由系统声明的,取名FILE。
- C编译环境一般都会提供的stdio.h头文件中就有相应的文件类型声明。
- 不同的C编译环境相应的文件类型声明也会不同。不过大同小异
// 结构体类型声明
typedef struct
{
short level; // 缓冲区“满”或“空”的程度
unsigned flags; // 文件状态标志
char fd; //文件描述符
unsigned char hold; //文件描述符
short bsize; // 缓冲区的大小
unsigned char * buffer; // 数据缓冲区的位置
unsigned char * curp; // 文件位置标记指针当前的指向
unsigned istemp; // 临时文件指示器
short token; // 用于有效性检查
}FILE;
从用户角度来看,程序是通过文件名来操作文件的
从程序设计人员来看,这个文件名就是结构体类型为FILE的一个结构体变量,因此就可以通过相关语法操作该变量即可。
定义结构体变量:
FILE f1
// 一般不使用此方式,而是通过定义结构体变量的指针来引用结构体变量不过注意的是一般不会通过变量名来引用这些变量。 而是通过设置一个指向FILE类型变量的指针变量,然后通过这个指针变量来引用这些FILE类型的变量。
FILE * fp; //定义一个FILE类型的指针变量
这样可以是fp指向某一个文件的文件信息区(是一个结构体变量),通过该文件信息区中的信息就能够访问该文件。
即通过文件指针变量能够找到与它相关联的文件
这种指向文件信息区的指针变量简称为指向文件的指针变量。
注意:指向文件的指针变量不是指向外部介质上的数据文件的开头,而是指向内存中的文件信息区的开头。类似于结构体变量的指针和结构体数组的指针一样。
打开文件与关闭文件
- 打开文件:就是指为文件建立相应的信息区和文件缓冲区。
- 信息区:用来存放有关文件的信息
- 文件缓冲区:用来暂时存放输入输出的数据。
在编写程序时,在打开文件的同时,一般都指定一个指针变量指向该文件
也就是建立起指针变量与文件之间的联系(即通过fopen函数的返回值赋给一个指针变量),
这样就可以通过该指针变量对文件进行读写了
- 所谓关闭是指撤销文件信息区和文件缓冲区,使文件指针变量不再指向该文件,显然就无法进行对文件的读写了。
用fopen函数打开数据文件
ANSI C规定了用标准输入输出函数fopen来实现打开文件
fopen函数的调用方式:
fopen(文件名,使用文件方式)// 返回的是指向某个文件的指针
/* 建立起指针变量与文件之间的联系,
即fp指向了al文件
*/
FILE * fp; // 定义指向FILE类型的指针变量fp
fp = fopen("al","r") // 将fopen函数的返回值赋给指针变量fp
/*
- 从上面的代码可以看出,在打开一个文件时,通知编译系统有3个信息:
- (1)需要打开文件的名字,也就是准备访问的文件名字
- (2)使用文件的方式(“读”还是“写”等)
- (3)让哪一个指针变量指向被打开的文件
- 使用文件方式共有12种
- 这三个是基本的:r只读,w只写,a是追加,
- 修饰的(不单独存在的):b表示二进制,+表示为了读和写(和+组合后rwa都可以写了)
文件使用方式 | 含义 | 如果指定的文件不存在 |
---|---|---|
r(只读) | 为了输入数据,打开一个已存在的文本文件 | 出错 |
w(只写) | 为了输出数据,打开一个文本文件 | 建立新文件 |
a(追加) | 向文本文件尾添加数据 | 出错 |
rb(只读) | 为了输入数据,打开一个二进制文件 | 出错 |
wb(只写) | 为了输出数据,打开一个二进制文件 | 建立新文件 |
ab(追加) | 向二进制文件尾添加数据 | 出错 |
“r+”(读写) | 为了读和写,打开一个文本文件 | 出错 |
“w+”(读写) | 为了读和写,建立一个文本文件 | 建立新文件 |
“a+”(读写) | 为了读和写,打开一个文本文件 | 出错 |
“rb+”(读写) | 为了读和写,打开一个二进制文件 | 出错 |
“wb+”(读写) | 为了读和写,建立一个新的二进制文件 | 建立新文件 |
“ab+”(读写) | 为了读和写,打开一个二进制文件 | 出错 |
- 如果不打开一个文件,fopen函数会返回一个空指针值NULL这个常用来判断一个文件是否正常打开
if((fp=fopen("file","r")) == NULL)
{
printf("cannot open this file\n");
exit(0);
}
- 带b和不带b的区别只有一个,即对换行的处理。
- 带b,即以二进制文件方式,系统不对换行处理
- 不带b,即以文本文件方式,系统就会对换行进行处理(不同系统处理的方法又会不同)
- windows系统,遇到换行符'\n'会把他转换为‘\r’和'\n'两个字符
- Linux则不会这么处理,还是会以'\n'处理
- 程序中可以使用3个标准的流文件——标准输入流、标准输出流和标准出错输出流
程序运行时,系统会自动打开这3个标准流文件。系统也定义了这三个文件的指针变量分别为stdin,stdout,stderr
用fclose函数关闭数据文件
关闭文件用fclose
fclose(文件指针)
fclose函数也带回一个值,当成功地执行了关闭操作,则返回值为0;否则返回值EOF为 -1 先关闭文件,然后再结束程序运行。否则会丢失缓冲区的数据 fclose函数关闭文件时,先把缓冲区中的数据输出到磁盘文件,然后才撤销文件信息区。
有的编译系统会自动先将缓冲区中的数据写到文件,避免这类问题的发生。但是最好还是先关闭文件,在结束程序。
顺序读写数据文件
顺序读写,即对文件读写数据的顺序和数据在文件中的物理顺序是一致的。顺序读写需要用库函数实现
怎样向文件读写字符
读写一个字符的函数
函数名 | 调用形式 | 功能 | 返回值 |
---|---|---|---|
函数名 | 调用形式 | 功能 | 返回值 |
fgetc | fgetc(fp)或getc(fp) | 从fp指向的文件读入一个字符 | 读成功,带回所读的字符;若失败,则返回文件结束标志EOF(即-1) |
fputc | fputc(ch,fp)或putc(fp) | 把字符ch写到文件指针变量fp所指向的文件中 | 输出成功,返回值就是输出的字符;输出失败,则返回EOF(即-1) |
- fgetc第一个字母f表示文件(file),get表示获取,c表示字符(character);因此fgetc的含义很清楚:从文件读取一个字符
- fputc,与上面类似 C系统已在头文件中把fputc和fgetc函数定义为宏名putc和getc,即类似别名
exit为标准C的库函数,作用是使程序终止。此 函数在stdio.h头文件里面
- 在文件的所有有效字符后有一个文件尾标志。
- 当读完全部字符后,文件读写位置标记就指向最后一个字符的后面,即指向了文件尾标志。
- 此时如果在执行读取操作,则会读出-1(不要理解为最后有一个结束字节,在其中存放了数值-1.它只是一个处理方法)。
- 文件尾标志用**标识符EOF(end of file)**表示,EOF在stdio.h文件中被定义为-1
- 用feof函数可以检测文件尾标志是否已被读过。
feof(指针)// 这个指针为指向文件的指针变量,即fopen函数的返回值
- 如果文件尾标志已被读出,则表示文件已结束,此时feof函数值为真(用1表示)
- 否则feof函数值为假(用0表示)
- 注意feof函数值的真(1)假(0)与文件尾标志的假设值的(-1)区别。feof函数值是函数值,后者为尾标志的假设值
怎样向文件读写一个字符串
- C语言允许通过函数fgets和fputs一次读写一次字符串
fgets(str,n,fp);
从fp所指向的文件中读入一个长度为n-1的字符串,并在最后加一个'\0'字符,
然后把这n个字符存放到字符数组str中
函数名 | 调用形式 | 功能 | 返回值 |
---|---|---|---|
函数名 | 调用形式 | 功能 | 返回值 |
fgets | fgets(str,n,fp) | 从fp指向的文件读入一个长度为(n-1)的字符串,存放到 字符数组str中 | 读成功,返回地址str;若失败,则返回NULL |
fputs | fputc(str,n,fp) | 把str所指向的字符串写到文件指针变量fp所指向的文件中 | 输出成功,返回值就是0;输出失败,则非0值 |
- fgets和fputs这两个函数的功能类似于gets和puts函数,但是gets和puts是以终端为读写对象,而fgets和fputs函数是以指定的文件为读写对象的。
(1) fgets函数的函数原型为
char * fgets(char * str,int n,FILE * fp);
- 注意实际上从指针所指向的文件中只读入n-1个字符,然后再最后加个'\0'字符,这样就得到了n个字符
- 如果在读完n-1个字符之前就遇到了换行符或者文件结束符EOF,则读入结束,但将遇到的换行符也作为一个字符读入。
- fgets函数执行成功,返回值为str数组首元素的地址。如果失败,则返回NULL
(2)fputs函数的函数原型为
int fputs(char * str,FILE * fp);
- 第一个参数可以是字符串常量,字符数组名或字符型指针
- 字符串末尾的'\0'不输出
- 若输出成功,函数为0;失败是,函数值为EOF(-1)
用格式化的方式读写文本文件
fprintf函数和fscanf函数:与printf和scanf函数的不同在于读写对象不同,一个是文件,后者是终端
fprintf(文件指针,格式字符串,输出表列);
fscanf(文件指针,格式字符串,输入表列);
fprintf和fscanf函数的优缺点
- 优点:用fprintf和fscanf函数对磁盘文件读写,使用方便,容易理解
- 缺点:由于在输入时要将文件中的ASCII码转换为二进制形式再保存在内存变量中。
输出时又要将内存中的二进制形式转换成字符,要花费较多时间。 因此,在内存与磁盘频繁交换数据的情况下,最好不要使用fprintf和fscanf函数。
用二进制方式向文件读写一组数据
在程序中不仅需要一次输入输出一个数据,有时也需要一次输入输出一组数据(如数组或结构体变量的值)
- C语言允许用fread函数从文件中读一个数据块 ,用fwrite函数向文件写一个数据块
fread(buffer,size,count,fp);
fwrite(buffer,size,count,fp);
在读写时是以二进制形式进行的。
在向磁盘写数据时,直接将内存中一组数据原封不动的、不加转换的复制到磁盘文件上,在读入时也是将磁盘文件中若干字节的内容一批读入内存。
- fread和fwrite的调用形式说明
- buffer:是一个地址,
- 对于fread来说,它是用来存放从文件读入的数据的存储区的地址
- 对于fwrite来说,是要把此地址开始的存储区中的数据向文件输出
- 这个地址指的都是起始地址
- size :要读写的字节数
- count:要读写多少个数据项(每个数据项长度为size)
- fp:FILE类型指针,指向文件的指针变量。
- 概念区分
- (1)数据的存储方式
- (2)文件的分类
- (3)文件的打开方式
- (4)文件读写函数
随机读写数据文件
随机访问不是按数据在文件中的物理位置次序进行读写,而是可以对任何位置上的数据进行访问,显然这种方法比顺序访问效率高得多。
文件位置标记及其定位
- (1)文件位置标记
- (2)文件位置标记的定位
可以强制使文件位置标记指向人们指定的位置。可以用下面的函数实现。
- 用rewind函数使文件位置标记重新返回文件的开头,次函数没有返回值。
- 用fseek函数改变文件位置标记
fseek(文件类型指针,位移量,起始点)- 用ftell函数测定文件位置标记的当前位置
随机读写
文件读写的出错检测
C提供一些函数用来检查输入输出函数时可能出现的错误
-
(1)ferror函数
-
(2)clearerr函数