第八章-文件

文件

所谓文件一般指存储在外部介质上数据的集合

文件有不同的类型,在程序设计中,主要用到两种文件:

  1. 程序文件。包括源程序文件、目标文件、可执行文件等。这种文件的内容是程序文件
  2. 数据文件。文件的内容不是程序,而是供程序运行时读写的数据,如在程序运行过程中输出到磁盘的数据,或在程序运行过程中供读入的数据。

输入输出是数据传送的过程,数据如流水一样从一处流向另一处,因此常将输入输出形象地称为,即数据流

表示了信息从目的端的流动。

C的数据文件由一连串的字符组成,而不考虑行的界线,两行数据间不会自动加分隔符,对文件的存取是以字符为单位的。输入输出数据流的开始和结束仅受程序控制而不受物理符号的控制。

文件名

一个文件要有唯一的文件标识,以便用户识别和引用。文件标识称为文件名。文件的标识包括3个部分:

  1. 文件路径
  2. 文件名主干
  3. 文件后缀

如:D:\CC\temp\file.txt

文件的分类

​ 根据数据的组织形式,数据文件可分为ASCII文件二进制文件。数据在内存中是以二进制形式存储的,如果不加转换地输出到外存,就是二进制文件,可以认为它就是存储在内存的数据的映像,所以也称之为映像文件。如果要在外寸上以ASCII代码形式存储,则需要在存储前进行转换。ASCII文件又称为文本文件,每一个字节放一个字符的ASCII代码。

文件缓冲区

​ ASCII标准采用"缓冲文件系统"处理数据文件,所谓缓冲文件系统是指系统自动地在内存区为程序中每一个正在使用的文件开辟一个文件缓冲区

文件类型指针

​ 缓冲文件系统中,关键的概念是文件类型指针简称文件指针

定义一个指向文件型数据的指针变量:

FILE * fp;

定义fp是一个指向FILE类型数据的指针变量。也就是说,通过文件指针变量能够找到与它关联的文件

为了方便起见,通常将这种指向文件信息区的指针变量简称为指向文件的指针变量。

注意:指向文件的指针变量并不是指向外部介质上的数据文件的开头,而是指向内存中的文件信息区的开头。

打开与关闭文件

所谓打开是指为文件建立相应的信息区和文件缓冲区。

关闭是指撤销文件信息区和文件缓冲区,使文件指针变量不再指向该文件,显然就无法进行对文件的读写。

用fopen函数打开数据文件

调用方式:fopen(文件名,使用文件方式);

例如:fopen("a1","r");

fopen函数的返回值是指向a1文件的指针。通常将fopen函数的返回值赋给一个指向文件的指针变量。

如:

FILE * fp;    // 定义一个指向文件的指针变量fp
fp=fopen("a1","r");  // 将fopen函数的返回值赋给指针变量fp

三条信息:

  1. 需要打开的名字,也就是准备访问的文件的名字

  2. 使用文件的方式

  3. 让哪一个指针变量指向被打开的文件

    使用文件的方式

    文件使用方式含义如果指定的文件不存在
    “r”(只读)为了输入数据,打开一个已存在的文本文件出错
    “w”(只写)为了输出数据,打开一个文本文件建立新文件
    “a”(追加)向文本文件尾添加数据出错
    “rb”(只读)为了输入数据,打开一个二进制文件出错
    “r+”(读写)为了读和写,打开一个文本文件出错
    “w+”(读写)为了读和写,建立一个新的文本文件建立新文件
    “a+”(读写)为了读和写,打开一个文本文件出错
    “rb+”(读写)为了读和写,打开一个二进制文件出错
  • "r"方式打开的文件只能用于向计算机输入而不能用作向该文件输出数据,而且该文件应该已经存在,并存有数据。不能用"r"打开一个并不存在的文件,否则出错。

  • 用"w"方式打开的文件只能用于向该文件写数据,而不能用来想计算机输入。如果原来不存在该文件,则在文件打开文件前建立一个以指定的名字命名的文件。如果该文件已存在,则打开前删除该文件,然后重新建立一个新文件。

  • 打开文件时,文件读写的位置标记记移到文件末尾。

  • 如果不能实现"打开"的任务,fopen函数将会带回一个出错的信息。出错的原因是:(1)用"r"方式打开一个并不存在的文件;(2)磁盘出故障;(3)磁盘已满无法建立新文件等。

    常用下面的方法打开一个文件:

    if((fp=fopen("file.dat","r"))==NULL)
    {
        printf("Cannot open this file.\n");
        exit(0);
    }
    

    exit函数作用是关闭所有文件,终止正在执行的程序,待用户检查出错误,修正后重新运行。

  • 标准输出流是从终端的输入

  • 标准输出流是向终端的输出

  • 标准出错输出流是当程序出错时将出错信息发送到终端

用fclose函数关闭数据文件

关闭就是撤销文件信息区和文件缓冲区,使文件指针变量不再指向该文件。

形式:fclose(文件指针);

例如:fclose(fp);

如果不关闭文件将会丢失数据。要用fclose函数关闭文件,先把缓冲区中的数据输出到磁盘文件,然后才撤销文件信息区。

fclose函数也带回一个值,当成功地执行了关闭操作,则返回值为0,否则返回EOF(-1)。

顺序读写数据文件

向文件读写字符

读写一个字符的函数

函数名调用形式功能返回值
fgetcfgetc(fp)从fp指向的文件读入一个字符读成功,带回所读的字符,失败则返回文件结束标志EOF(-1)
fputcfputc(fp)把字符ch写到文件指针变量fp所指向的文件中输出成功,返回值就是输出的字符;失败返回EOF

说明:fgetc的第一个字母f代表文件(file),中间的get表示"获取",最后一个字母c表示字符,fgetc的含义很清楚:从文件读取一个字符。fputc也类似。

程序举例:将一个磁盘文件中的信息复制到另一个磁盘文件中。

#include <stdio.h>
#include <stdlib.h>

int main()
{
    FILE *in,*out;                  // 定义指向FILE类型文件的指针变量
    char ch,infile[10],outfile[10]; // 定义两个字符数组,分别存放两个数据文件名
    printf("请输入读入文件的名字:");
    scanf("%s",infile);
    printf("请输入输出文件的名字:");
    scanf("%s",outfile);
    if((in=fopen(infile,"r"))==NULL)
    {
        printf("无法打开此文件\n");
        exit(0);
    }
    if((out=fopen(outfile,"w"))==NULL)
    {
        printf("无法打开此文件\n");
        exit(0);
    }
    while(!feof(in))                  // 如果为遇到输入文件的结束标志
    {
        ch=fgetc(in);
        fput(ch,out);                 // 将ch写到输出文件中
        putchar(ch);                  // 将ch显示到屏幕上
    }
    putchar(10);                      // 显示完全部字符后换行
    fclose(in);
    fclose(out);
    return 0;
}

说明:为了知道对文件的访问是否完成,只需看文件读写的位置是否移到文件的末尾。用feof函数可以检查到文件读写位置标记是否移到文件的末尾,即磁盘文件是否结束。

###向文件读写一个字符串

读写一个字符串的函数

函数名调用形式功能返回值
fgetsfgets(str,n,fp)从fp指向的文件读入一个长度为(n-1)的字符串,存放到字符数组str中读成功,返回地址str,失败则返回NULL
fputsfputs(str,fp)把str所指向的字符串写到文件指针变量fp指向的文件中输出成功,返回0,否则返回非0值。

说明

  • fgets函数原型为:char * fgets(char *str, int n, FILE *fp);

    例如:fgets(str,n,fp);

    作用是从fp所指向的文件读入一个长度为(n-1)的字符串,并在最后加上’\0’的字符,然后把这n个字符存放到字符数组str中。

  • fputs函数原型为:int fputs(char *str, FILE *fp);

    其作用是把str所指向的字符串写到文件指针变量fp指向的文件中。

  • fputs和fgets这两个函数的功能类似于gets和puts函数,只是gets和puts以终端为读写对象,而fgets和fputs函数以指定的文件为的读写对象。

程序举例:从string.dat中读取字符串,并在屏幕显示。

#include <stdio.h>
#include <stdlib.h>

int main()
{
    FILE *fp;
    char str[3][10];
    int i=0;
    if((fp=fopen("D:\\CC\\string.dat","r"))==NULL)   // 注意文件路径
    {
        printf("Canno open this file!\n");
        exit(0);
    }
    while(fgets(str[i],10,fp)!=NULL)
    {
        printf("%s",str[i]);
        i++;
    }
    fclose(fp);
    return 0;
}

用格式化的方式读写文件

fprintf函数和fscanf函数,它们的作用是对文件进行格式化读写。

一般调用方式:

fprintf(文件指针,格式字符串,输出表列);

fscanf(文件指针,格式字符串,输出表列);

例如:fprintf(fp,"%d,%6.2f",i,f);

它的作用是将int型变量i和float型变量f的值按%d和%6.2f的格式输出到fp指向的文件中。

若i=3,f=4.5,则输出到磁盘文件上的是:3, 4.50

用fprintf和fscanf函数对磁盘文件读写,使用方便,容易理解,但由于在输入时要将文件中的ASCII码转换为二进制形式再保存到内存变量中,在输出时又要将内存中的二进制形式转换为字符,要花费较多时间。因此,在内存与磁盘频繁交换数据的情况下,最好不用。

用二进制方式向文件读写一组数据

用fread函数从文件读一个数据块,用fwrite函数向文件写一个数据块。在读写时是以二进制形式进行的。

一般形式:

fread(buffer,size,count fp);

fwrite(buffer,size,count fp);

buffer:是一个地址。对fread来说,它是用来存放从文件读入的数据的存储区的地址。对fwrite来说,是要把此地址开始的存储区中的数据向文件输出。

size:要读写的字节数。

count:要读写多少个数据项。

fp:FILE类型指针。

例如:fread(f,4,10,fp);

其中f是一个float型数组名。这个函数从fp指向的文件读入10个4个字节的数据,存储到数组f中。

如果有一个Struct student_type结构体类型:

struct Student_type
{
    char name[10];
    int num;
    int age;
    char addr[30];
}stud[40];

假设学生的数据已存放在磁盘文字中,可以用下面的for语句和fread函数读入40个学生的数据:

for(i=0;i<40;i++)
	fread(&stud[i],sizeof(struct Student_type),1,fp);

执行40次循环,每次从fp指向的文件中读入结构体数组stu的一个元素。

同样,以下for语句和fwrite函数可以将内存中的学生数据输出到磁盘文件中:

for(i=0;i<40;i++)
	fwrite(&stud[i],sizeof(struct Student_type),1,fp);

fread或fwrite函数的类型为int型,如果fread或fwrite函数执行成功,则函数返回值为形参count的值,即输入或输出数据项的个数。

程序举例:输入10个学生的有关数据,然后把它们转存到磁盘上。

#include <stdio.h>
#define SIZE 0

struct Student_type
{
    char name[10];
    int num;
    int age;
    char addr[30];
}stud[SIZE];

void save()
{
    FILE *fp;
    int i;
    if((fp=fopen("stu.dat","wb"))==NULL)
    {
        printf("Cannot open file!\n");
        return;
    }
    for(i=0;i<SIZE;i++)
    {
        if(fwrite(&stud[i],sizeof(struct Student_type),1,fp)!=1)
            printf("file write error!\n");
        fclose(fp);
    }
    
    int main()
    {
        int i;
        printf("Please enter data of students:\n");
        for(i=0;i<SIZE;i++)
            scanf("%s%d%d%s",stud[i].name,&stud[i].num,&stud[i].age,&stud[i].addr);
        save();
        return 0;
    }
}

如果企图从"stu.dat"文件以“w”方式写入数据会出错。

因为fread和fwrite函数一般用于二进制文件的输出输入。

随机读写数据文件

文件位置标记及其定位

1.文件位置标记

​ 为了对读写控制,系统为每一个文件设置了一个文件读写位置标记,用来指示"接下来要读写的下一个字符的位置"。

如果文件位置标记是按字节位置顺序移动的,就是顺序读写。如果能将文件位置标记按需要移动到任意位置,那就是随机读写。所谓随机读写,是指读写完上一个字符后,并不一定要读写其后续的字符,而可以读写文件中任意位置上所需要的字符。即对文件读写数据的顺序和数据在文件中的物理顺序一般是不一致的。

2. 文件位置的标记的定位
  1. 用rewind函数使文件位置标记指向文件开头

    rewind函数的作用是使文件位置标记重新返回文件的开头,此函数没有返回值。

    例如:rewind(fp);

  2. 用fseek函数改变文件位置标记

    调用形式:fseek(文件类型指针,位移量,起始点);

    起始点用0,1,2代替。

    起始点名字用数字代替
    文件开始位置SEEK_SET0
    文件当前位置SEEK_CUR1
    文件末尾的位置SEEK_END2

    位移量指以起始点为基点,向前移动的字节数。位移量应该是long型数据。

    fseek函数一般用于二进制文件。

    例:fseek(fp,100L,0); // 将文件位置标记向前移到文件开头100字节处

    fseek(fp,50L,1); // 将文件位置标记向前移到当前位置50字节处

  3. 用ftell函数测定文件位置标记的当前位置

    ftell函数的作用是得到流式文件中文件位置标记的当前位置。

    由于文件中的文件位置标记经常移动,所以常用ftell函数得到当前位置,用相对于文件开头的位移量来表示。如果调用函数时出错,ftell函数返回值为-1L。

    i=ftell(fp);

    if(i==-1L) printf("error!\n");

    随机读写

    程序举例:在磁盘文件上存有10个学生的数据。要求将第1,3,5,7,9个学生数据输入计算机,并显示。

    #include <stdio.h>
    #include <stdlib.h>
    
    struct Student_type
    {
        char name[10];
        int num;
        int age;
        char addr[15];
    }stud[10];
    
    int main()
    {
        int i;
        FILE *fp;
        if((fp=fopen("stu.dat","rb"))==NULL)
        {
            printf("Cannot open file\n");
            exit(0);
        }
        for(i=0;i<10i+=2)
        {
            fseek(fp,i*sizeof(struct Student_type),0);    // 移动文件位置标记
            fread(&stud[i],sizeof(struct Student_type),1,fp);  // 读一个数据块到结构体变量
            printf("%-10s %4d %4d %-15s\n",stu[i].name,stu[i].num,stu[i].age,stu[i].addr);
        }
        fclose(fp);
        return 0;
    }
    

    文件读写的出错检测

    1. ferror函数

    ​ 在调用各种输入输出函数时,如果出现错误,除了函数返回值有所反映外,还可以用ferror函数检查。

    一般形式:ferror(fp);

    如果ferror函数返回值为0,表示未出错;如果返回一个非零值,表示出错。

    应该注意,对同一个文件每一次调用输入输出函数,都会产生一个新的ferror函数值,因此,应当在调用一个输入输出函数后立即检查ferror函数的值,否则信息会丢失。

    在执行fopen函数时,ferror函数的初始值自动置为0。

    2. clearerr函数

    ​ clearerr函数的作用是使文件错误标志和文件结束标志置为0.假设在调用一个输入输出函数时出现错误,ferror函数值为一个非零值。应立即调用clearerr(fp),是ferror(fp)的值变成0,以便下一次进行检测。

    ​ 只要出现文件读写错误标志,它就一直保留,直到对同一文件调用clearerr函数或rewind函数,或任何其他输入输出的函数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值