C语言学习笔记—文件操作

前言

  • 文件是当今计算机系统不可或缺的部分。文件用于储存程序、文档、数据、书信、表格、图片、视频和许多其他种类的信息。作为程序员,必须会编写创建文件和从文件读写数据程序。
  • 操作文件的正确流程为:打开文件 --> 读写文件 --> 关闭文件。文件在进行读写操作之前要先打开,使用完毕要关闭。
  • 在C语言中,为了统一对各种硬件的操作,简化接口,不同的硬件设备也都被看成一个文件。对这些文件的操作,等同于对磁盘上普通文件的操作。

fopen和fclose函数 

  • fopen函数原型:

  1.  主要功能是打开文件。需要头文件:stdio.h
  2. 函数声明如下:
    FILE *fopen(const char *filename, const char *mode); //使用给定的模式 mode 打开 filename 所指向的文件
  3. filename指要打开的文件名称(可包含相对路径和绝对路径),mode指文件的访问模式。模式如下表:
    模式                                                                                         描述
       r                                                           打开一个用于读取的文件。该文件必须存在
      r+                                               打开一个用于更新的文件,可读取也可写入。该文件必须存在
     rb+                                                  读写打开一个二进制文件,允许读写数据。文件必须存在
     rw+                                                                 读写打开一个文本文件,允许读和写
      w  创建一个用于写入的空文件。如果文件名称与已存在的文件相同,则会删除已有文件的内容,文件被视为一个新的空文件
      w+  创建一个用于读写的空文件。如果文件名称与已存在的文件相同,则会删除已有文件的内容,文件被视为一个新的空文件
      a                     追加到一个文件。如果文件存在,写操作向文件末尾追加数据。如果文件不存在,则创建文件
      a+           以附加方式打开可读写的文件。如果文件存在,写操作向文件末尾追加数据。如果文件不存在,则创建文件
  4. fopen打开文件成功,返回有效FILE的有效地址,失败返回NULL。
  • fclose函数:

  1. 文件一旦使用完毕,应该用 fclose() 函数把文件关闭,以释放相关资源,避免数据丢失。fclose() 的用法如下:
    int fclose(FILE *stream);   //关闭流 stream,刷新所有的缓冲区
  2. 如果流成功关闭,则该方法返回零。如果失败,则返回 EOF。
  3. 例子:
    #include <stdio.h>
    
    int main()
    {
       FILE *fp;
       fp = fopen("file.txt", "r");
       fprintf("打开一个只读文件file.txt");
       fclose(fp); 
       return(0);
    }

文本模式和二进制模式

  •  文本文件和二进制文件:

  1. 所有文件的内容都以二进制形式(0或1)储存的。
  2. 文本文件:如果文件最初使用二进制编码的字符(如ASCII或Unicode)表示文本(就像C字符串那样),该文件就是文本文件,其中包含文本内容。
  3. 二进制文件:如果文件中的二进制代表机器语言代码或数值数据(使用相同的内部表示,假设,用于long或double类型的值)或图片或音乐编码,该文件就是二进制文件,其中包含二进制内容。
  • 文本模式和二进制模式:

  1. 在二进制模式中,程序可以访问文件的每个字节。而在文本模式中,程序所见的内容和文件的实际内容不同。
  2. 在windows系统中,文本模式下,文件以" \r\n "代表换行。若以文本模式打开文件,并用fput等函数写入换行符" \n "时,函数会自动在" \n "前面加上" \r "。在windows读写一个二进制文件时,读写模式中参数一定要加" b ",防止系统添加无谓的" \r ",但读写一个文本文件时,就不要加" b ",这样可以不用单独处理" \r "。
  3.   在类Unix/Linux系统中,文本模式下,文件以" \n "代表换行,所以linux系统中在文本模式和二进制模式下并无区别。linux下读写模式中参数" b "是无效的,可忽略。

getc和putc函数

  • getc函数原型:

  1.  

stat函数

  •  函数原型:

  1. 主要获得文件属性。需要头文件:sys/types.h, sys/stat.h, unistd.h。
  2. 函数声明如下:
    int stat(const char *file_name, struct stat *buf ); // 通过文件名filename获取文件信息,并保存在buf所指的结构体stat中
  3. 执行成功则返回0,失败返回-1,错误代码存于errno,包括:
    ENOENT         参数file_name指定的文件不存在
    ENOTDIR        路径中的目录存在但却非真正的目
    ELOOP          欲打开的文件有过多符号连接问题,上限为16符号连接
    EFAULT         参数buf为无效指针,指向无法存在的内存空间
    EACCESS        存取文件时被拒绝
    ENOMEM         核心内存不足
    ENAMETOOLONG   参数file_name的路径名称太长数值
  4. stat结构体里面成员信息:
    struct stat {
        dev_t         st_dev;       //文件的设备编号
        ino_t         st_ino;       //节点
        mode_t        st_mode;      //文件的类型和存取的权限
        nlink_t       st_nlink;     //连到该文件的硬连接数目,刚建立的文件值为1
        uid_t         st_uid;       //用户ID
        gid_t         st_gid;       //组ID
        dev_t         st_rdev;      //(设备类型)若此文件为设备文件,则为其设备编号
        off_t         st_size;      //文件字节数(文件大小)
        unsigned long st_blksize;   //块大小(文件系统的I/O 缓冲区大小)
        unsigned long st_blocks;    //块数
        time_t        st_atime;     //最后一次访问时间
        time_t        st_mtime;     //最后一次修改时间
        time_t        st_ctime;     //最后一次改变时间(指属性)
  5. 例子:
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <unistd.h>
    
    int main()
    {
        struct stat st = { 0 };
        stat("a.txt", &st);
        int size = st.st_size; // 得到文件的大小
        printf("%d\n", size);
        return 0;
    }
  • stat,fstat,lstat函数区别:

  1. fstat,lstat函数原型:
    int fstat(int filedes, struct stat *buf);
    int lstat(const char *path, struct stat *buf);
  2. 这三个函数的功能是一致的,都用于获取文件相关信息,但应用于不同的文件对象。对于stat函数中给出pathname参数,stat函数返回与此命名文件有关的信息结构,fstat函数获取已在描述符fields上打开文件的有关信息,lstat函数类似于stat,但是当命名的文件是一个符号链接时,lstat返回该符号链接的有关信息,而不是由该符号链接引用文件的信息。

fread和fwrite函数

  • fread函数:

  1. fgets() 有局限性,每次最多只能从文件中读取一行内容,因为 fgets() 遇到换行符就结束读取。如果希望读取多行内容,需要使用 fread() 函数。
  2. fread() 函数用来从指定文件中读取块数据。所谓块数据,也就是若干个字节的数据,可以是一个字符,可以是一个字符串,可以是多行数据。
  3. 函数声明:其中,ptr 是指向带有最小尺寸 size*nmemb 字节的内存块的指针,可以是数组、变量、结构体等;size 是要读取的每个元素的大小,以字节为单位;nmemb  是元素的个数,每个元素的大小为 size 字节。stream  是指向 FILE 对象的指针,该 FILE 对象指定了一个输入流。
  4. size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) //从给定流 stream 读取数据到 ptr 所指向的数组中
  5. 返回值:返回成功读写的块数,也即 nmemb。如果返回值小于 nmemb,可能读到了文件末尾,可能发生了错误,可以用 ferror() 或 feof() 检测。
  • fwrite函数:

  1. 函数声明:其中,ptr 是指向要被写入的元素数组的指针;size 是要被写入的每个元素的大小,以字节为单位;nmemb 是元素的个数,每个元素的大小为 size 字节。stream 是指向 FILE 对象的指针,该 FILE 对象指定了一个输出流。
    size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) //把 ptr 所指向的数组中的数据写入到给定流 stream 中
  2. 返回值:返回成功读写的块数,也即 nmemb。如果返回值小于 nmemb,肯定发生了写入错误,可以用 ferror() 函数检测。
  • feof函数:

  1. 函数声明:
    int feof(FILE *stream)  //测试给定流 stream 的文件结束标识符
  2. 返回值:当设置了与流关联的文件结束标识符时,该函数返回一个非零值,否则返回零。
  • 例子:文件的复制

  1. WIN下输入:a a.avi b.avi  ,可将a.avi复制到b.avi。由于一次只读一个,导致读取的时间很长,需优化。
    /**** a.c ****/
    #include <stdio.h>
    
    int main(int argc, char **args)
    {
        if(argc < 3)
            return 0;
        FILE *p = fopen(args[1], "rb");
        if(p == NULL)
            return 0;
        FILE *p1 = fopen(args[2], "wb");
        if(p1 = NULL)
            return 0;
        while(1)
        {
            char a = 0;
            fread(&a, 1, 1, p);
            if(feof(p))
                break;
            fwrite(&a, 1, 1, p1);
        }
        fclose(p);
        fclose(p1);
        return 0;
    }
  2. WIN下输入:a a.avi b.avi  ,可将a.avi复制到b.avi。由于一次读1024个字节,虽优化了时间,但可能导致复制过来的文件不一致,因为不能保证所有文件都是1024的倍数。
    /**** a.c ****/
    #include <stdio.h>
    
    int main(int argc, char **args)
    {
        if(argc < 3)
            return 0;
        FILE *p = fopen(args[1], "rb");
        if(p == NULL)
            return 0;
        FILE *p1 = fopen(args[2], "wb");
        if(p1 = NULL)
            return 0;
        while(1)
        {
            char a[1024] = { 0 };
            fread(a, 1, sizeof(a), p);
            if(feof(p))
                break;
            fwrite(a, 1, sizeof(a), p1);
        }
        fclose(p);
        fclose(p1);
        return 0;
    }
  3. WIN下输入:a a.avi b.avi  ,可将a.avi复制到b.avi。采用堆的方式动态分配内存,极大缩短时间,为了防止文件过大,添加个阈值:最大一次只能发64K字节。
    /**** a.c ****/
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/stat.h>
    
    #define NUM 1024*64  // 64K
    
    int main(int argc, char **args)
    {
        if(argc < 3)
            return 0;
        FILE *p = fopen(args[1], "rb");
        if(p == NULL)
            return 0;
        FILE *p1 = fopen(args[2], "wb");
        if(p1 = NULL)
            return 0;
        
        struct stat st = { 0 };
        stat(args[1], &st);
        int size = st.st_size;    // 得到文件大小
        if(size >= NUM)
            size = NUM;
        char *buf = malloc(size);  // 根据文件的小,动态分配内存
    
        while(!feof(p))
        {
            int res = fread(buf, 1, size, p);
            fwrite(buf, 1, res, p1);
        }
        free(buf);
        fclose(p);
        fclose(p1);
        return 0;
    }
  • fseek函数:

  1. 函数声明:
    int fseek(FILE *stream, long int offset, int whence) //设置流 stream 的文件位置为给定的偏移 offset,参数 offset 意味着从给定的 whence 位置查找的字节数
  2. 其中,stream 是指向 FILE 对象的指针。offset 是相对 whence 的偏移量,以字节为单位。whence 是表示开始添加偏移 offset 的位置。它一般指定为下列常量之一:
    常量描述
    SEEK_SET文件的开头
    SEEK_CUR文件指针的当前位置
    SEEK_END文件的末尾
  3. 返回值:如果成功,则该函数返回零,否则返回非零值。
  4. 例子:
    #include <stdio.h>
    
    int main()
    {
        FILE *p1 = fopen("a.dat", "w");
        char b[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        fwrite(b, 1, sizeof(b), p1);
        fclose(p1);
        
        FILE *p = fopen("a.dat", "r");
        fseek(p, 2, SEEK_SET); //将文件指针从开始位移2个字节
        char a[2];
        fread(a, 1, sizeof(a), p);
        printf("%d, %d\n", a[0],a[1]);  // 输出结果:3, 4
        fseek(p, -2, SEEK_END); //将文件指针从末尾向初始位置方向位移2个字节
        fread(a, 1, sizeof(a), p);
        printf("%d, %d\n", a[0],a[1]);  // 输出结果:9, 10
        fclose(p);
        return 0;
    }
  5.  
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值