14-C语言文件操作

文件基本概念

  • 文件分类

    • 文本文件

      • 以ASCII码格式存放,一个字节存放一个字符.
    • 二进制文件

      • 以二进制存储的
  • 文本文件和二进制文件的区别

    • 1.存储步骤不同
      • 文本文件在存储的时候,会先查询需要存储数据的ASCII码, 然后再将对应的ASCII 码转换为二进制,然后再存储;
    • 2.从存储步骤来看,文本文件需要县查找再存储, 所以效率会低一些
    • 3.从内存上的表现, 文本文件会更占用内存
  • 注意点:
    • 任何的文本编辑器在打开文件的时候,都会以某种编码方式去解码存储的数据
    • 而文本编辑器默认的解码方式就是ASCII码解码

文件的打开和关闭

  • fputc/ fputs/ fgetc/ fgets 这些函数都是用于操作文本文件的;
  • fwrite / fread 这两个函数就是用于操作二进制文件的
  • 也就是说:
    • fputc/ fputs/ fgetc/ fgets保存的都是 ASCII码
    • fwrite / fread 保存的是二进制

fopen()函数

  • fopen函数返回的FILE * 是一个指向结构体的指针;
  • FILE *并不是打开的那个文件, 而是一个结构体,这个结构体中描述了被打开文件在缓冲区中的各种状态
  • 函数的声明: FILE * fopen ( const char * filename, const char * mode );
    • 两个参数都是传入字符串
    • 第一个参数: 需要打开的文件的名称
    • 第二个参数: 打开文件的模式
    • 返回值: 如果打开文件成功, 会返回文件的句柄, 如果打开失败会返回NULL
    • 模式的对应取值
    • r 读取文件
      • 文件不存在会报错,文件存在就打开
      • 只能读不能写
      FILE *fp = fopen("abc.txt", "r");
      if(fp == NULL){
          printf("打开失败\n");
      }
      
    • w 写入文件
      • 文件不存在会自动创建, 文件存在会直接打开
      • 只能写不能读
      FILE *fp = fopen("abc", "r");
      if(fp == NULL){
          printf("打开失败");
      }
      
    • r+ 读取和写入文件
      • 文件不存在会报错,文件存在就会打开
      • 既可以读又可以写
      FILE *fp = fopen("abc.txt", "r+");
      if(fp == NULL){
          printf("打开失败");
      }
      
    • w+ 读取和写入文件(重点)
      • 文件不存在会自动创建, 文件存在会直接打开
      • 既可以读也可以写
      FILE *fp = fopen("abc.txt", "w+");
      if(fp == NULL){
          printf("打开失败\n");
      }
      
    • a 追加写入文件
      • 文件不存在会自动创建, 文件存储会直接打开
      • 只能在原有数据的基础上追加
      FILE *fp = fopen("abc.txt", "a");
      if(fp == NULL){
          printf("打开失败\n");
      }
      
    • a+ 追加写入文件,并且可以读取(重点)
      • 文件不存在会自动创建, 文件存在会直接打开
      • 可以在原有数据的基础上追加, 并且还可以读取数据
      FILE *fp = fopen("abc.txt", "a+");
      if(fp == NULL){
          printf("打开失败\n");
      }
      

fclose()函数

  • 格式: fclose(fp);
    • 第一个参数: 需要关闭的文件句柄
    • 返回值: int 成功返回0, 失败返回 EOF(-1);
  • 一次写入一个字符 (fputc函数)
  • fputc就是写入一个字符
    • fputc是以文本文件的形式保存数据
    • 格式: int fputc (int ch, FILE * stream );
    • 第一个参数: 需要写入到文件的字符
    • 第二个参数: 已经打开文件句柄;
    • 返回值:写入成功,返回写入成功的字符, 如果失败, 返回 EOF
    • EOF 是文件结束的标识,本质上就是-1;
    FILE *fp = fopen("abc.txt", "w");
    int res = fputc('a', fp);
    printf("res = %i\n", res);
    
    // 关闭打开的文件
    fclose(fp);
    

一次读取一个字符(fgetc()函数)

  • int fgetc ( FILE * stream );
    • 第一个参数: 被读取的文件的 文件句柄
    • 返回值:当前获取到的字符, 如果获取失败就会返回 EOF
    FILE *fp = fopen("abc.txt", "w+");
    fputc('a',fp);
    fputc('b',fp);
    fputc('c',fp);
    fputc('d',fp);
    
    // 每次操作完文件,文件的指针都会向后移动
    // 将文件指针重新只想一个流的开头
    rewind(fp);
    // fgetc 被读取的文件的 文件句柄
    char ch ;
    while((ch = fgetc(fp)) != EOF){
        printf("ch = %c\n", ch);
    }
    
    

文件末尾的判断(feof()函数)

  • feof()函数(了解一下)

    • 返回值为0, 没有读到文件末尾
    • 如果返回非0, 代表读到了文件末尾
    #include <stdio.h>
    
    int main()
    {
        FILE *fp = fopen("abc.txt", "w+");
        fputc('a',fp);
        fputc('b',fp);
        fputc('c',fp);
        fputc('d',fp);
    
        // 将文件指针重新只想一个流的开头
        rewind(fp);
        
        // 按照下列的写法, 会多读取一个字符
        // 原因就是feof函数在判断文件指针时, 指针还没有移动
        // 只有写入或者读取过来指针才会移动
        
        char ch ;
    //    while(!feof(fp)){
    //        ch = fgetc(fp);
    //        printf("ch = %c\n", ch);
    //    }
    
        // 所以在使用feof函数的时候, 一定要先取, 然后再判断feof
        // 否者会多取一个
        while((ch = fgetc(fp)) && !feof(fp)){
            printf("ch = %c\n", ch);
        }
    
        return 0;
    }
    
    
  • 注意点:在使用feof函数的时候,一定要先取 ,然后再判断Feof, 否则会夺取一个

  • 定义函数, 实现文件的加密和解码

#include <stdio.h>
void encode(char *name, char *newName, int code);
int main()
{
    // 编码
    //encode("abc.txt", "encode.txt", 6);
    // 解码
    encode("encode.txt", "decode.txt", 6);
    return 0;
}


void encode(char *name, char *newName, int code){
    // 打开一个需要加密的文件
    FILE *fr = fopen(name, "r+");
    // 打开需要写入加密内容的文件
    FILE *fw = fopen(newName, "w+");
    // 不断的读  不断的加密 不断的写入
    char ch = EOF;
    while((ch = fgetc(fr)) != EOF){
        ch = ch ^ code;
        fputc(ch, fw);
    }
    // 关闭已经打开的文件
    fclose(fr);
    fclose(fw);
}



一次性写入一行数据(fputs()函数)

  • fputs()函数可以一次写入一堆字符
  • 格式: fputs(const char * restrict _Str,FILE * restrict _File);
    • 第一个参数: 需要写入的数据
    • 第二个参数: 写入到那个文件的文件句柄
  • 注意点: 不会再写入字符的后面自动添加\n
// 打开一个文件
FILE *fp = fopen("abc.txt", "w+");
// 一次写入一堆函数:需要自己添加换行 \n
fputs("www.baidu.com\n", fp);
fputs("www.taobao.com\n", fp);
// 关闭打开的文件
fclose(fp);

一次性读取一行数据(fgets()函数)

  • fgets()函数 格式: fgets(char * restrict _Buf,int _MaxCount,FILE * restrict _File);

    • 第一个参数: 读取出来的数据会放到这个参数的变量中
    • 第二个参数: 需要读取多少个字节的数据
    • 第三个参数: 被读取文件的句柄
    • 注意点: 虽然告诉系统需要读取1024个字符,但是只要遇到\n, fgets函数就会自动终止读取
    FILE *fp = fopen("abc.txt", "r+");
    char buf[1024];
    fgets(buf, 1024, fp);
    printf("buf = %s\n", buf);
    // 关闭打开的文件
    fclose(fp);
    
  • 注意点:

    • 遇到\n自动结束,并且\n 也会被读取进来
    • maxCount 指定多睡不一定会读取多少, 读取到\n会自动停止读取
    • 最多只能读取 maxCount - 1 个字符, 会在最后自动添加 \0
    • 遇到EOF也会自动停止读取
    #include <stdio.h>
    
    int main()
    {
        FILE *fp = fopen("abc.txt", "r+");
        char buf[1024];
    
        /*
         * abc.txt  : hello world
         *            www.baidu.com
        */
        // 每次只能读取n-1个字符, 会在末尾自动添加\0
        fgets(buf, 4, fp);
        printf("buf = %s\n", buf);  // hel
    
        fgets(buf, 1024, fp);
        printf("buf = %s\n", buf);
    
        // 关闭打开的文件
        fclose(fp);
    
        return 0;
    }
    
    
  • 遍历写法

// 推荐写法
    while(fgets(buf, 1024, fp) != NULL){
        printf("buf = %s\n", buf);
    }

// 不推荐写法
    while(fgets(buf, 1024, fp) && !feof(fp)){
        
        printf("buf = %s\n", buf);
    }

一次性写入一块数据(fwrite()函数)

  • 格式: fwrite(const void * restrict _Str,size_t _Size,size_t _Count,FILE * restrict _File);
    • 第一个参数: 需要写入文件的数据地址
    • 第二个参数: 每次写入多少个字节
    • 第三个参数: 需要写入多少次
    • 第四个参数: 写入到上面地方
    • 返回值: 写入了多少次就返回多少, 出错或文件结束, 返回 0;
FILE *fp = fopen("abc.txt", "w+");
int ages[] = {1, 3, 5};
printf("sizeof(ages) = %i\n", sizeof(ages));
fwrite(&ages, sizeof(ages), 1, fp);
  • 注意点:
    • 一般情况下, 写入一块数据,每次写入多少个字节可以写大一点, 而写入多少次会写小一点;
    • 由于fwrite是以二进制的形式写入文件的, 所以和以文件的写入不同, fwrite函数会忽略 \0 \n等内容;
FILE *fp = fopen("abc.txt", "w+");
char *str = "hello\0world";
// fputs是以文本文件的形式存储, 存储的是ASCII
// 注意点: 如果利用fputs写入字符串, 遇到\0会停止写入
//    fputs(str, fp);
// 注意点: 在二进制中是没有\n \0这些概念的, 写入的时候不会受到\0 \n的影响
fwrite(str, 9, 1, fp);

一次性读取一块数据(fread()函数)

  • 格式: fread(void * restrict _DstBuf,size_t _ElementSize,size_t _Count,FILE * restrict _File);
    • 第一个参数: 读取的数据存储到哪
    • 第二个参数: 一次读取多少个字节
    • 第三个参数: 读取多少次
    • 第四个参数: 从什么地方读取
    • 返回值: 读取了多少就返回多少, 如果返回0 代表读取错误
FILE *fp = fopen("abc.txt", "w+");
// abc.txt :  hello world
char buf[1024] = {0};
// fp对应的文件中读取1024次,每次读取1个字节, 将读取的内容放到buf中
fread(buf, 1, 1024, fp);
printf("buf = %s\n", buf);
  • 注意点:
    • fread(buf, 1, 1024, fp); 虽然告诉系统需要读取1024次,但是只要读取到EOF就不会读取了
    • 每次读取多少个字节, 一定要按照存储的数据类型占用的内存大小来读取, 不要写过大的值
FILE *fp = fopen("abc.txt", "wb+");
char *str1 = "123456";
fwrite(str1, sizeof(char), strlen(str1), fp);
rewind(fp);
char buf[1024] = {0};
while(fread(buf, 1, 1024, fp) > 0){
    printf("buf = %s\n", buf);
}

读写结构体

  • 读单个结构体
#include <stdio.h>
typedef struct person{
    char *name;
    int age;
    double height;
} Person;
int main()
{
//    // 写入一个结构体
//    Person p = {"www", 18, 1.78};
//    FILE *fp = fopen("abc.txt", "wb+");
//    fwrite(&p, sizeof(Person), 1, fp);
//    // 关闭文件
//    fclose(fp);

    // 读一个结构体
    FILE *fp = fopen("abc.txt", "rb+");
    Person p;
    fread(&p, sizeof(Person), 1, fp);
    printf("name = %s\n", p.name);
    printf("age = %i\n", p.age);
    printf("height = %lf\n", p.height);
    // 关闭文件
    fclose(fp);
    return 0;
}

  • 读写结构体数组
#include <stdio.h>
typedef struct person{
    char name[8];
    int age;
    double height;
} Person;
int main()
{

//        // 写入一个结构体数组
//        Person ps[3] = {
//            {"www", 38, 1.38},
//            {"baidu", 18, 1.78},
//            {"com", 98, 1.58},
//        };
//        FILE *fp = fopen("abc.txt", "wb+");
//        fwrite(&ps, sizeof(ps), 1, fp);
//        // 关闭文件
//        fclose(fp);



        // 读一个结构体数组
        FILE *fp = fopen("abc.txt", "rb+");
        Person ps[3];
        fread(&ps, sizeof(Person), 3, fp);
        for(int i = 0; i< 3; i++){
            Person p = ps[i];
            printf("name = %s\n", p.name);
            printf("age = %i\n", p.age);
            printf("height = %lf\n", p.height);
            printf("----------\n");
        }
         // 关闭文件
        fclose(fp);


    return 0;
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351

推荐阅读更多精彩内容