第五天2017/04/06(上午:文件的“读写操作”)

这里写图片描述

文件的“读”、“写”的理解
    不是站在“程序”的角度,而是站在“文件”的角度
    读:从文件中读取数据,输出到程序中
    写:从程序中读取数据,写入到文件中

(1)在C程序中系统对文件进行的各种操作是通过指向文件结构体的指针变量实现的。
    定义指向文件结构欧体类型的指针变量的形式:
    FILE* 指针变量名;
    例如:  FILE  *fp1 , *fp2;
(2)文件的打开
    C语言文件的打开是通过stdio.h函数库的fopen()函数实现的。
    fopen()函数的原型是:FILE* fopen(char *filename,char *mode);
    其中:filename是要打开的文件的文件名,mode是说明处理文件的方式。
        打开文件的调用方式是:文件指针变量=fopen(文件名,处理文件方式);
        例如:  fopen(“a:\\exam1.c”,”r”)
    【如果文件不存在,造成打开文件失败】
        FILE *pf = fopen("a:\\exam1.c","r");
        if(pf==NULL) //如果返回值是NULL,则表示文件不存在,打开文件失败
        {
            cout<<"文件不存在,打开文件失败"<<endl;
            exit(-1);  //exit(0)表示正常退出,exit(非0)表示程序出错后退出
        }
(3)文件的读写操作(当文件打开后,最常见的操作就是对文件的读取和写入操作)
下面介绍四种常用的文件的读取和写入操作的函数
                          从文件读取   |  向文件写入
    ①按  字符  读写的函数: fgetc()    |  fputc()     读写“一个”字符
    ②按  字符串读写的函数: fgets()     |  fputs()
    ③按格式要求读写的函数: fprintf()   |  fscanf()
    ④按  数据块读写的函数: fread()     |  fwrite()
上面常见的四种函数功能介绍以及应用举例
①fgetc()    |  fputc()
调用形式:fgetc(文件型指针类型)  例:char ch = fgetc(fp);
    //作用:从一个文件中读取一个字符
    //返回值:返回所读取的一个字节。如果读到文件末尾或者读取出错时返回EOF。
调用形式:fputc(字符,文件型指针类型);  例:fputc('A',fp);
    //执行过程:将字符‘A’(也可以是字符型变量)写入文件的当前位置,并且使指向文件当前位置的指针下移一个字节。
    //返回值:如果写入成功,返回的是该字符的ASCII值; 如果写入失败,返回EOF
②fgets()     |  fputs()
调用形式:char* fgets(字符数组,字符数,文件型指针变量);  char *fgets(char *buf, int bufsize, FILE *stream);
    //作用:从fp指向的文件的当前位置读取n-1个字符,再加上字符串结束标记'\0'(注意:字符串的结束标志‘\0’不写入文件中去),一起放入字符数组中。
特殊情况:fgets函数一旦遇到字符'\n',就会在'\n'后面加上字符串结束字符'\0',然后把之前读入包括'\n''\0'的字符数组存放到buf中
    //返回值:函数成功将返回字符数组的首地址; 失败或读到文件结尾返回NULL。
//【分析】如果开辟的buf过大,则不会出现任何问题,但是会浪费内存空间;但是如果开辟的buf过小,会导致。见下面例子:
假设文件中有下面两行字符串:
Love,I Have
Since you can do it.
//先用fgets(str1,6,file1);去读取,则读取后 str1 = “Love,\0”
//再执行fgets(str1,20,file1);去读取:此时从I开始读取,读取19个字节,但是由于提前遇到'\n'字符,会在'\n'后面加上'\0',因此读取后结果str1 = " I Have\n"
因此我们不能直接通过fgets的返回值来判断函数是否是出错而终止的,应该借助feof函数或者ferror函数来判断。

调用形式:int fputs(const char* str,FILE* fpout);

【使用实例1#include <stdio.h>  
#define LENGTH 100  
int main(void)  
{  
    FILE *fd;  
    char str[LENGTH];  
    //创建一个文件  
    fd = fopen("hello.txt","w+");  
    //往文件里面写入字符串“"hello.world"  
    if(fd)  
    {  
        fputs("hello,world",fd);  
    } 
    //关闭文件  
    fclose(fd);  
    //再次打开文件,并读出所有字符串  
    fopen("hello.txt","r");  
    fgets(str,LENGTH,fd);  
    //输出到标准输出设备  
    printf("%s\n",str);  
    //关闭文件  
    fclose(fd);  
}  
【使用实例2】   将文件d:\data1.txt中的内容复制到d:\data2.txt中。
{
    FILE *f_in, *f_out;   
    char str[50];
    f_out = fopen(“d:\\data1.txt”,"r");  //读取
    f_in  = fopen(“d:\\data2.txt”,"w");   //写入
    …… …… ……
    while(!feof(f_out))   //如果f_out遇到的不是字符串结束标志,则进入while循环
    {
        fgets(str,30,f_out); //f_out读取30个字符赋值给str
        fputs(str,f_in);     //把str写入到f_in打开的文件中
    }
    fclose(f_out);   
    fclose(f_in);
}
③fprintf()   |  fscanf()
调用形式:int fprintf(文件型指针变量, 格式控制, 输出表列);
//解释:函数fprintf()的作用与printf()相类似,只是输出对象不是“标准输出设备”而是“文件”,即按照格式要求“将数据写入文件”。
例如:  
    fprintf(fp,"%ld,%s,%5.1f",num,name,score);//作用:将变量num、name、score按照%ld、%s、%5.1f的格式写入fp指向的文件的当前位置

调用形式:int fscanf(FILE* stream, const char* format, [argument...]);翻译成int fscanf(文件型指针变量, 格式控制, 输出表列);
  例: 
    /*从文件中读取数据*/
    fscanf(stream,"%s",s);
    fscanf(stream,"%ld",&l);  //和scanf对比相类似,输出表列的变量加上 &
    fscanf(stream,"%f",&fp);
    fscanf(stream,"%c",&c);

④按数据块读写的函数fread()、fwrite()
调用形式:size_t  fread( void *buffer, size_t size, size_t count, FILE *stream) ;
buffer  buffer是一个变量,它用于存放从stream中读取到的数据.
size    要读的每个数据项的字节数,单位是字节byte.
count   要读count个数据项,每个数据项size个字节.
stream  输入流.
//作用:作用是从文件中读出成批的数据块。即:fread是一个函数。从一个文件流中读数据,最多读取
    //count个项,每个项size个字节,如果调用成功返回实际读取到的项个数(小于或等于count),如果不成功或读到文件末尾返回 0。
//返回值:如果函数fread()操作成功,则返回值为实际从文件中读取数据块的个数。

调用形式:fwrite(写入文件的数据块的“存放地址”,一个数据块所占的字节数,数据块的个数,文件型指针变量);
    size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream);
    //返回值是:写入数据块的个数(次数),因此由返回值可以判断是否读取到值!,见下面示例:
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
struct stu
{
    int age;
    char ch[100];
}st[3]={{20,"Tom"},{21,"Mary"},{23,"Faker"}};

int main()
{
    FILE *fp = fopen("D.txt","w+");
    if(!fp)
    {
        printf("打开失败\n");
        exit(-1);
    }
    //方式一:
    //while(fwrite(&st[0],sizeof(struct stu),3,fp) != 3)
    //{

    //}
    int count = fwrite(&st[0],sizeof(struct stu),  3,fp); //正确,因为st[]数组的大小就是3.
    //int count = fwrite(&st[0],sizeof(struct stu),300,fp); //错误,从st[]数组中读取数据时,会发生越界

    //方式二:读结构体数组时最好用方式二,逐个读取st[i]
    //for(int i=0;i<3;i++)
    //{
    //  if(fwrite(&st[i],sizeof(struct stu),1,fp) !=1 ) //第三个参数是1,表示一次读取一个st[i]
    //  {
    //      printf("写入失败,退出程序\n");
    //      exit(-1);
    //  }
    //}
    return 0;
}
    buffer:是一个指针,与fread(“存取数据的地址”)中不同,对fwrite来说,是
        要 “获取数据的地址”,例如buffer中存放着很多数据,把buffer中的数据写到stream打开的文件夹中。
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
struct stu
{
    int age;
    char ch[100];
}st[3];

int main()
{//为了验证上面fwrite是否把结构体数组中存放的数据写入到文件C.txt中去,
    ///我们可以用下面的程序进行测试:用一个文件指针fp以“r”的方式打开文件,
    ///把读取到的数据存放到&st[i]中,然后把是st[i]中的数据输出,看下
    ///输出结果是不是上面fwrite写入的数据?如果是,表明写入成功;如果不是,
    ///表明写入失败。
    FILE *fp = fopen("C.txt","r+");
    if(!fp)
    {
        printf("打开失败\n");
        exit(-1);
    }
    方式一:
    //  if(fread(&st[0],sizeof(struct stu),3,fp) !=3 ) //第三个参数是3,一次读取3个st[i]
    //  {
    //      printf("写入失败,退出程序\n");
    //      exit(-1);
    //  }
    //  for(int i=0;i<3;i++)
    //  {
    //      printf("%5d,%s\n",st[i].age,st[i].ch);
    //  }

    //方式二   
    for(int i=0;i<3;i++)
    {
        if(fread(&st[i],sizeof(struct stu),1,fp) !=1 )
        {
            printf("写入失败,退出程序\n");
            exit(-1);
        }
        printf("%5d,%s\n",st[i].age,st[i].ch);

    }
    return 0;
}
【fread简单示例】
例如:已知stu1是一个结构体struct student变量,则
fread(&stu1, sizeof(struct student ), 1, fp);
表示的含义是从文件类型指针fp指向的文件的当前位置开始,读取1个数据块,该数
据块为结构体struct student类型变量所占字节数,然后将读取的内容放入变量stu1中。
【fwrite简单示例】
    例如:已知struct student 类型的数组stu[20],则语句fwrite(&stu[1], sizeof(struct student ), 2, fp);
表示的含义是 ——> 从结构体数组元素stu[1]存放的地址开始,以一个结构体struct student类型变量所占字
    节数为一个数据块,共写入文件类型指针fp指向的文件2个数据块,即stu[1]、stu[2]的内容写入文件。
    如果操作成功,函数的返回值为2。

【fread、fwrite综合示例1】创建一个文件并写入一段数据,然后读取出来。
#include <stdio.h>
    void main( void )
{
    FILE *stream;
    char list[30];
    int i, numread, numwritten;
    // 以文本方式打开文件
    if( (stream = fopen( "fread.out", "w+t" )) != NULL )  // 如果读取无误
    {
        for ( i = 0; i < 25; i++ )  //给list[i]数组的每个元素进行赋值
            list[i] = (char)('z' - i);

        //fwrite:从list数组中获取字符,要写入大小为sizeof(char)的字符,一共写入25次,写到stream打开的文件中,返回值是实际写入的次数
        numwritten = fwrite( list, sizeof( char ), 25, stream );
        printf( "Wrote %d items\n", numwritten );

        fclose( stream );
    }
    else
    {
        printf( "Problem opening the file\n" );
    }
    if( (stream = fopen( "fread.out", "r+t" )) != NULL )  // 文件读取
    {
        numread = fread( list, sizeof( char ), 25, stream );
        printf( "Number of items read = %d\n", numread );
        printf( "Contents of buffer = %.25s\n", list );
        fclose( stream );
    }
    else
    {
        printf( "File could not be opened\n" );
    }
}
运行结果:
    Wrote 25 items
    Number of items read =25
    Contents of buffer = zyxwvutsrqponmlkjihgfedcb

【fread、fwrite综合示例2】将10个学生记录输入文件d:\stu中,然后显示。
struct ST
{
    char class[6];
    …………
}a[N],stu;  //定义一个结构体数组a[N]和结构体变量stu

int main()
{
    int i;   
    FILE *fp = fopen("d:\\stu","wb"); //写入方式打开文件夹
    for(i=0;i<N;i++) //给结构体数组a[N]中的每个元素输入数据
    {
        printf("\n\t请输入班级:"); 
        scanf("%s",a[i].class);
        …… ……  ……    
    }
    if(fwrite(a,sizeof(struct ST),N,fp)!=N)  //用fwrite函数写入往fp打开的文件中写入大小为sizeof(struct ST),个数为N的数据
    {//如果fwrite返回的值不等于N
        printf("文件不能写入数据.\n");//那么,打印这句话 
        exit(1);  //异常退出
    }
    fclose(fp);//关闭fp打开的文件夹,此时fp已经不指向之前打开的文件夹

    fp=fopen("d:\\stu","rb");  //以读取方式打开 “上面刚写入数据的文件夹d:\\stu” 
    for(i=0;i<N;i++)
    { 
        if(fread(&stu,sizeof(struct ST),1,fp)= =1) //从fp打开的文件读取1个以stu的地址为起始地址,大小为sizeof(struct ST),个数为1的数据,存放到&stu中去
            printf("\t%6s%10ld%9s%5d%6d%8d\n",stu.class,… … … );//printf在屏幕上格式化输出
        else  //如果返回值不是1
        {
            printf("文件不能读取数据.\n");  
            exit(1); //则异常退出
        }
    }
    fclose(fp);   
}

(4)文件的关闭(当文件操作结束后,为了防止误操作等操作破坏打开的文件,应该在使用后及时的关闭)
    具体做法:
        fclose(文件指针fp);  //则将fp指向的文件关闭,fp不在指向之前被打开的文件


【拓展】文件中常用的函数 fseek()、feof()、rewind()、ftell()

fseek():int fseek(FILE *stream, long offset, int fromwhere);等价于fseek(文件型指针变量,偏移量,起始位置);
/*int fseek( FILE *stream, long offset, int origin);
第一个参数stream为文件指针
第二个参数offset为偏移量,正数表示正向偏移,负数表示负向偏移

第三个参数origin设定从文件的哪里开始偏移,可能取值为: SEEK_SET、SEEK_CUR、 SEEK_END 
SEEK_SET: 文件开头
SEEK_CUR: 当前位置
SEEK_END: 文件结尾
其中SEEK_SET,SEEK_CUR和SEEK_END依次为0,1和2.
简言之:
fseek(fp,100L,0);把stream指针移动到离文件开头100字节处;
fseek(fp,100L,1);把stream指针移动到离文件当前位置100字节处;
fseek(fp,-100L,2);把stream指针退回到离文件结尾100字节处。
*/
函数设置文件指针stream的位置:
    如果执行成功,stream将指向以fromwhere为基准,偏移offset(指针偏移量)个字节的位置,函数返回0。
    如果执行失败(比如offset超过文件自身大小),则不改变stream指向的位置,函数返回一个非0值。


feof():用来检测一个指向文件的指针是否已经指到了文件最后的结束标志EOF。
    调用的一般形式为:  feof(文件型指针变量);
      如果文件型指针指向的文件的当前位置为结束标志EOF,则函数返回一个非零值,否则返回0值。
rewind():将令指向文件的指针重新指向文件的开始位置。函数无返回值。
    其调用形式为:   rewind(文件型指针变量);
      例如: rewind(fp);//fp是一个指向文件的指针,执行该语句后,fp指向文件的开始位置,即文件的第一个数据。 
ftell():用于测试指向文件的指针的当前位置。
    调用方式为:      ftell(文件型指针变量);
    //函数的返回值是一个常整型数,如果测试成功,则返回指向文件的指针当前指向的位置距离文件开头的字节数,否则返回-1L 

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
int main()
{//按字符
    FILE *fp = fopen("A.txt","a+"); //a+表示追加
    char ch[] = "ABCDDCBA";
    int i=0;
    while(ch[i]!='\0')
    {
        if(fputc(ch[i],fp)==0)
        {
            printf("func:fput error");
            exit(-1);
        }
        i++;
    }
    fclose(fp);
    getchar();
}
int main()
{//按行
    FILE *fp = NULL;
    char *filename = "code.txt"; 
    if((fp = fopen(filename,"r"))==NULL) //“r”打开文件
    {
        printf("func:fopen() err\n");
        return 0;
    }
    char ch[300];
    memset(ch,0,sizeof(char)*300);
//从fp打开的文件中读取数据,显示到窗口中有以下两种方式
//方式一
    //while(fgets(ch,300,fp)) //逐行读取fp打开的文件,把读到的数据存放到ch[]中
    //{
    //  printf("%s\n",ch);//注意:此时自己加上字符 '\n'
    //  memset(ch,0,sizeof(char)*300);
    //}
//方式二:(这种方式比方式一好!)
    while(!feof(fp))  //此处是!feof(fp),不要忘记感叹号'!'
    {
        fgets(ch,300,fp); //逐行读取fp打开的文件,把读到的数据存放到ch[]中
        printf("%s\n",ch);
        memset(ch,0,sizeof(char)*300);
    }
    fclose(fp);

    getchar();
}


【总结】文件操作
1、C语言文件读写概念建立以及文件读写API的熟悉
API的种类
一、文件读写API
    fgetc    fgetc  按照字符读写文件
    fputs    fgets  按照行读写文件(读写配置文件)
    fread    fwrite 按照块读写文件(大数据块迁移)
    fprintf  fscanf 按照格式化读写文件

二、文件控制API
    文件是否结束
    文件指针的定位、跳转
三、用API做项目
2、项目开发,配置文件读写库的设计与实现
3、项目开发:大数据文件加密解密设计与实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值