提示:今天的学习笔记是学习文件及其操作和IO缓冲区
目录
一 文件
1.文件的基本知识:什么是文件、文件名、文件的分类、文件缓冲区、文件类型指针
什么是文件:“文件”一般指存储在外部介质上数据的集合。操作系统是以文件为单位对数据进行管理的 。
文件名:一个文件要有一个唯一的文件标识,以便用户识别和引用。文件标识包括3部分:(1)文件路径;(2)文件名主干;(3)文件后缀。文件路径表示文件在外部存储设备中的位置。如:D:\CC\temp\filel.dat
文件的类型1. 程序文件。包括源程序文件(后缀为.C)、目标文件(后缀为.obj)、可执行文件(后缀为.exe)等。这种文件的内容是程序代码。2. 数据文件。 ①ASCII文件(文本文件),一个字节放一个字符②二进制文件。
文件的类型:所谓缓冲文件系统是指系统自动地在内存区为程序中每一个正在使用的文件开辟一个文件缓冲区。从内存向磁盘输出数据必须先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘去。如果从磁盘向计算机读入数据,则一次从磁盘文件将一批数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(给程序变量)。缓冲区的大小由各个具体的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* fp;
2.文件的打开与关闭
对文件读写之前应该“打开”该文件,所谓“打开”是指在内存中为文件建立相应的信息区(用来存放有关文件的信息)和文件缓冲区(用来暂时存放输入输出的数据)。
所谓“关闭”是指撤销内存中的文件信息区和文件缓冲区,使文件指针变量不再指向该文件,显然就无法进行对文件的读写了。
a.用fopen函数打开数据文件:fopen(文件名,使用文件方式);
FILE* fp;
fp = fopen("al","r");
fp指向了al文件。可以看出,在打开一个文件时,通知编译系统以下3个信息:
1. 需要打开文件的名字,也就是准备访问的文件的名字;
2. 使用文件的方式(“读”还是“写”等);
3. 让哪一个指针变量指向被打开的文件。
b.用fclose函数关闭数据文件:关闭文件用fclose函数。fclose函数调用的一般形式为:fclose(文件指针);
例如:
fclose(fp);
fclose函数也带回一个值,当成功地执行了关闭操作,则返回值为0;否则返回EOF(即-1)。
对安全打开文件函数fopen_s()的介绍:打开文件的更安全的可选备用函数是fopen_s(),其原型是:
fopen_s(FILE * restrict pfile,const char* restrict name,const char* restrict mode);要使用这个函数,需要把_STDC_WANT_LIB_EXT1_符号定义为1。fopen_s()函数的第一个参数是一个FILE结构指针的指针,所以把文件指针变量的地址传递给他。如果一切正常,函数就返回0;如果因某种原因不能打开文件,返回NULL。
特别提示:对fopen_s()函数的使用注意事项:
因为fopen_s()函数一切正常,返回0,如果因某种原因不能打开文件,则返回NULL,所以进行条件判断时,分为两种情况,
(1) 一种是判断打开文件的返回值是0(即是否正常打开)。
if (fopen_s(&fp, filename, "r")==NULL) //打开输入文件,判断文件是否正常打开
{
printf("无法打开此文件\n");
exit(0);
}
(2) 另一种是判断文件指针是否为空。
fopen_s(&fp, filename, "w"); //打开输出文件并使 fp 指向此文件
if (fp == NULL) //判断文件指针变量的值
{
printf("无法打开此文件"); //如果打开时出错, 就输出“ 打不开”的信息
exit(0); //终止程序
}
二 顺序写入文件
- 从文件中读写字符
- 从文件中读写字符串
- 用格式化的方式从文件中读写
-
用二进制方式向文件中读写一组数据
1.从文件中读写字符
2. 从文件中读写字符串:fgets函数在如果在读完n-1个字符之前遇到换行符“\n”或文件结束符EOF,读入即结束,但将所遇到的换行符“\n”也作为一个字符读入。fputs函数中第一个参数可以是字符串常量、字符数组名或字符型指针。字符串末尾的“\0”不输出。
3.用格式化的方式从文件中读写:
fprintf(文件指针,格式字符串,输出表列);
fscanf(文件指针,格式字符串,输入表列);
安全函数如下:
fprintf_s(文件指针,格式字符串,输出表列);
fscanf_s(文件指针,格式字符串,输入表列);
4.用二进制方式向文件中读写一组数据
fread(buffer, size, count, fp);
fwrite(buffer, size, count, fp);
使用说明:
a. buffer:是一个地址。对fread来说,它是用来存放从文件读入的数据的存储区的地址。对fwrite来说,是要把此地址开始的存储区中的数据向文件输出(以上指的是起始地址)。
b. size:要读写的字节数。
c. count:要读写多少个数据项(每个数据项长度为size)。
d. fp:FILE类型指针
三 随机读写数据文件
- 文件位置标记及其定位
- 文件位置标记
- 文件位置定位
- 随机读写例题
1. 文件位置标记及其定位
对文件进行顺序读写比较容易理解,也容易操作,但有时效率不高,例如文件中有1000个数据,若只査第1000个数据,必须先逐个读入前面999个数据,才能读入第1000个数据。如果文件中存放一个城市几百万人的资料,若按此方法查某一人的情况,等待的时间可能是不能忍受的。随机访问不是按数据在文件中的物理位置次序进行读写,而是可以对任何位置上的数据进行访问,显然这种方法比顺序访问效率高得多。
2.文件位置标记
为了对读写进行控制, 系统为每个文件设置了一个文件读写位置标记,文件位置标记或文件标记 用来指示“接下来要读写的下一个字符的位置”。一般情况下,在对字符文件进行顺序读写时,文件位置标记指向文件开头,如图所示。这时如果对文件进行读的操作,就读第1个字符,然后文件位置标记向后移一个位置,在下一次执行读的操作时,就将位置标记指向的第2个字符读入。依此类推,直到遇文件尾,结束。
3. 文件位置定位
a.文件位置定位函数:用rewind函数使文件位置标记指向文件开头,此函数没有返回值。
b.用fseek函数改变文件位置标记
fseek(文件类型指针,位移量,起始点)
使用说明:
(1)起始点如图所示
(2)“位移量”指以“起始点”为基点,向前移动的字节数。位移量应是long型数据
fseek(fp,100L,0); //将文件位置标记向前移到离文件开头 100 个字节处
fseek(fp,50L,1); //将文件位置标记向前移到离当前位置 50 个字节处
fseek(fp,-10L,2) //将文件位置标记从文件末尾处向后退 10 个字节
c.用ftell函数测定文件位置标记的当前位置
i = ftell(fp);
if(i=-1L)
printf("error\n");
4.随机读写例题:
在磁盘文件上存有10个学生的数据。要求将第1、3、
5、7、9个学生数据输入计算机,并在屏幕上显示出来。
#define _STDC_WANT_LIB_EXT1_ 1
#include<stdio.h>
#include<stdlib.h>
struct student
{
int num;
char name[10];
int age;
char addr[15];
}stud[10];
int main()
{
int i;
FILE* fp;
if (fopen_s(&fp, "stu.dat", "rb"))
{
printf("打不开文件.\n");
exit(0);
}
for (i = 0; i < 10;i += 2)
{
fseek(fp, i * sizeof(struct student), 0); //将文件标记从文件开始位置移动到i个结构体长度的位置
fread(&stud[i], sizeof(struct student), 1, fp); //从当前文件中读出一个长度为结构体字节的信息赋值给stud[i]变量
printf("stud[%d]:", i+1);
printf("\t%d\t%s\t%d\t%s\t\n", stud[i].num, stud[i].name, stud[i].age, stud[i].addr);
}
fclose(fp);
return 0;
}
四 文件读写的出错检测:
C提供一些函数用来检查输入输出函数调用时可能出现的错误。
1. ferror函数
在调用各种输入输出函数(如putc,getc,fread,fwrite等)时,如果出现错误,除了函数返回值有所反映外,还可以用ferror函数检査。它的一般调用形式为
ferror(fp);
如果ferror返回值为0(假),表示未出错;如果返回一个非零值,表示出错。
在执行fopen函数时,ferror函数的初始值自动置为0。
2. clearerr函数
clearer的作用是使文件错误标志和文件结束标志置为0。假设在调用一个输人输出函数时出现错误,ferror函数值为一个非零值。应该立即调用clearerr(fp),使ferror(fp)的值变成0,以便再进行下一次的检测。
只要出现文件读写错误标志,它就一直保留,直到对同一文件调用clearerr函数或rewind函数,或任何其他一个输入输出函数。
五 IO缓冲区
我们把常见的显示器、硬盘、键盘、鼠标等都称之为IO设备,I指输入的意思,O指输出的意思。合起来就是输入输出设备的意思。IO设备和CPU设备速度差距比较大的,我们为了协调IO设备和CPU的设备速度不匹配的问题,我们就设置了IO缓冲区。
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
FILE *fp;
if((fp=fopen("output.txt","w"))==NULL)
{
perror("打开文件失败,原因是");
exit(EXIT_FAILURE);
}
fput("Ilove FishC.com\n",fp);
getchar();
fclose(fp);
return 0;
}
IO缓冲区其实有点像内存池。
标准的IO缓冲区提供了三种类型的缓冲模式:按块缓存,按行缓存,不缓存。按块缓存也称为全缓存,即在填满缓冲区后才进行实际的设备读写操作;按行缓存是指在接收到换行符('\ n ')之前,数据都是先缓存在缓冲区的;最后一个是不缓存,也就是允许你直接读写设备上的数据。
不写用缓冲区直接写入设备用fflush函数,使用来改变缓存模式setvbuf来改变缓存模式。