• C语言文件操作(1)


    🐵本篇文章将会对文件相关知识进行讲解


    1. 为什么要使用文件❓

    当我们写了一个简单的项目比如一个通讯录程序,我们添加完成员信息并关闭程序后,这些成员的信息也会随着程序的结束而不复存在了;所以为了使得数据持久化,我们将数据存放到文件中,因为文件是放在硬盘上的,数据不会丢失

    2. 什么是文件❓

    磁盘上的文件就是文件,在程序设计中我们讨论的文件有两种:程序文件,数据文件

    2.1 程序文件

    程序文件包括源程序文件(.c)目标文件(.obj)可执行程序(.exe)

    2.2 数据文件

    文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件或者输出内容的文件(.txt就是数据文件

    比如将通讯录成员的信息写到文件中,将文件中的信息读取出来;这里说的文件就是数据文件

    2.3 文件名

    每一个文件都要有唯一的文件标识也就是文件名,以便用户识别

    文件名包含3部分:文件路径+文件主干名+文件后缀比如D:\code\data.txt

    3. 文件的打开和关闭📂

    3.1 文件指针

    每一个使用过的文件都在内存上开辟了一块文件信息区,文件信息区实际上就是一个结构体,这个结构体用来存放文件的各种信息(比如:文件的名字、状态、位置等)该结构体类型是系统声明的,取名为:FILE

    每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并自动填充其中的信息,使用者不必关心细节
    一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。下面我们可以创建一个FILE* 的指针变量:

    FILE* pf

    pf就是一个指向FILE类型的指针,可以使pf指向某个文件的文件信息区(也可以说是一个结构体变量)通过pf可以找到文件信息区的信息,进而访问该文件;通过文件指针变量能够找到与它关联的文件

    3.2 文件的打开和关闭

    fopen函数原型:

    fopen是以mode的方式打开文件filename,filename是文件名,可以字符串的形式表示,第二个参数是打开文件的方式,也是以字符串的形式表示

    文件使用方式含义如果指定文件不存在
    r(只读)为了(输入)读取数据,打开一个已存在的文本文件报错
    w(只写)为了(输出)写入数据,打开一个文本文件建立一个新的文件

    fclose函数原型:

    参数stream是指向某个文件的指针,关闭文件后要将stream赋为空指针,以防止野指针

    实例演示:

    1. int main()
    2. {
    3. FILE* pf = fopen("data.txt", "w");
    4. if (pf == NULL)
    5. {
    6. perror("fopen");
    7. return 1;
    8. }
    9. fclose(pf);
    10. pf == NULL;
    11. return 0;
    12. }

    当使用fopen打开文件data.txt后,系统会自动创建一个FILE类型的变量,并向这个变量填充信息,之后将这个变量的地址传给pf

    如果在当前路径(与.c文件相同的路径)没有data.txt,系统会自动创建一个同名文件

    文件路径:文件路径有两种:绝对路径和相对路径

    相对路径中.\\表示当前路径,..\\表示上一级路径

    FILE* pf = fopen(".\\data.txt", "r"); //相对路径(此时是当前路径)

    FILE* pf = fopen("C:\\Project\\C\\study\\study_10_8\\data.txt", "r"); //绝对路径(完整表述文件位置的路径)

    4. 文件的顺序读写📋

    功能函数适用于
    字符输入函数fgetc所有输入流
    字符输出函数fputc所有输出流

    4.1 流

    向不同的外部设备传输数据的方法是不同的,为了简化这种传输方式,引入了流,我们可以将数据传输到流中,然后流会将数据传输到对应的外部设备里

    流有文件流,标准输入流,标准输出流,标准错误流

    操作文件前要先打开文件,操作完文件后要关闭文件,在使用scanf和printf时为什么没有打开键盘和打开屏幕的操作,这是因为C语言程序只要运行起来就会自动打开三个流:标准输入流(stdin)标准输出流(stdout)标准错误流(stderr),这三个流的类型也都是FILE*

    4.2 函数讲解

    fgetc函数原型:

    fgetc用于读取文件的一个字符,stream是一个FILE*类型的指针,也可以理解为是一个流,fgetc如果读取失败则会返回EOF

    实例演示:

    事先向data.txt文件中输入abcdef

    1. int main()
    2. {
    3. FILE* pf = fopen("data.txt", "r");//读文件就要以只读的方式打开文件
    4. //写文件就要以只写的方式打开
    5. if (pf == NULL)
    6. {
    7. perror("fopen");
    8. return 1;
    9. }
    10. int ch = 0;
    11. while ((ch = fgetc(pf)) != EOF)
    12. {
    13. printf("%c", ch);
    14. }//结果为abcdef
    15. fclose(pf);
    16. pf = NULL;
    17. return 0;
    18. }

    fputc函数原型:

    fputc用于向文件输入(写)一个字符,character是要输入的那一个字符,stream是一个FILE*类型的指针,也可以理解为一个流

    实例演示:

    1. int main()
    2. {
    3. FILE* pf = fopen("data.txt", "w");
    4. if (pf == NULL)
    5. {
    6. perror("fopen");
    7. return 1;
    8. }
    9. char ch = 0;
    10. for (ch = 'a'; ch <= 'z'; ch++)
    11. {
    12. fputc(ch , pf); //输出到文件(写文件)
    13. fputc(ch, stdout); //输出到屏幕上,此时和printf功能相同
    14. }
    15. fclose(pf);
    16. pf == NULL;
    17. return 0;
    18. }

    4.3 其他函数讲解

    功能函数适用于
    文本行输入函数fgets所有输入流
    文本行输出函数fputs所有输出流
    格式化输入函数fscanf所有输入流
    格式化输出函数fprintf所有输出流
    二进制输入fread文件流
    二进制输出fwrite文件流
    将格式化数据转化为字符串sprintf---
    将字符串转化为格式化数据sscanf---

    fgets函数原型:

    fgets函数用于文本行输入,每次读取流中一行数据,即如果流中有多行数据,在读取其中的某一行时,当读取到'\n'就停止读取;fgets会从流stream中读取num - 1个字符到str中

    1. FILE* pf = fopen("data.txt", "r");
    2. char arr[100] = { 0 };
    3. fgets(arr,num, pf);
    4. printf("%s", arr);

    事先在data.txt中输入hello<换行>world,当num是5时,意思是将pf中5 - 1 = 4个字符读取到arr中,arr的打印结果为hell,当num为100时,会读取到第一行的'\n'所以会将"hello\n"读取到arr中

    fputs函数原型:

    fputs用于文本行输出,将str的数据写到流stream中,直到遇到'\0'

    1. FILE* pf = fopen("data.txt", "w");
    2. fputs("abcdef\0abcd", pf);

    上述代码将"abcdef\0abcd"写到pf中,因为字符串中有'\0',所以只会将abcdef写到pf中,那么data.txt中的内容就是abcdef

    fscanf函数原型:

    fscanf用于格式化输入,直接看代码:

    1. struct S
    2. {
    3. float f;
    4. int i;
    5. char c;
    6. };
    7. FILE* pf = fopen("data.txt", "r");
    8. struct S s = { 0 };
    9. fscanf(pf, "%f %d %c", &(s.f), &(s.i), &(s.c));
    10. printf("%f %d %c", (s.f), (s.i), (s.c));

    事先在data.txt中输入3.14 5 c,上述代码就是读取pf中的数据以特定的格式到结构体s对应的变量中,最后打印结果为3.140000 5 c

    fprintf函数原型:

    fprintf函数用于格式化输出,直接看代码:

    1. struct S
    2. {
    3. float f;
    4. int i;
    5. char c;
    6. };
    7. FILE* pf = fopen("data.txt", "w");
    8. struct S s = {3.14, 5, 'c'};
    9. fprintf(pf, "%f %d %c", s.f, s.i, s.c);

    上述代码是将结构体变量初始化的数据以特定的格式写入文件data.txt中


    文件使用方式含义如果指定文件不存在
    rb(只读)为了输入数据,打开一个二进制文件出错
    wb(只写)为了输出数据,打开一个二进制文件建立一个新的文件

    fwrite函数原型:

    fwrite是对一个二进制文件写入数据,fwrite的功能是将ptr中的内容写到stream中,size是写入数据的字节大小,count是写入的元素个数

    1. FILE* pf = fopen("data.txt", "wb");
    2. int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    3. fwrite(arr, sizeof(int), sizeof(arr)/sizeof(arr[0]), pf);

    在data.txt中显示的内容是:

    这些数据我们无法理解其含义,所以要通过fread将该二进制文件中的数据读取出来并打印到屏幕上(我们向文件中写的是1~10,那么读取后打印到屏幕上的也应该是1~10)

    fread函数原型:

    fread是读取一个二进制文件中的数据,fread的功能是将stream中的数据读取到ptr中,stream中每一个数据的字节大小是size,一共读取count个元素

    1. FILE* pf = fopen("data.txt", "rb");
    2. int arr1[10] = { 0 };
    3. fread(arr, sizeof(int), 10, pf);
    4. int i = 0;
    5. for (i = 0; i < 10; i++)
    6. {
    7. printf("%d ", arr1[i]);
    8. }

    打印结果为:

    sprintf函数原型:

    sprintf用于将格式化的数据转化为字符串

    sscanf函数原型:

    sscanf用于将字符串转化为格式化数据

    1. struct S
    2. {
    3. float f;
    4. char c;
    5. int i;
    6. };
    7. int main()
    8. {
    9. struct S s = { 3.14, 'c', 100 };
    10. char arr[100] = { 0 };
    11. sprintf(arr, "%f %c %d", (s.f), (s.c), (s.i));
    12. printf("%s\n", arr);
    13. struct S t = { 0 };
    14. sscanf(arr, "%f %c %d", &(t.f), &(t.c), &(t.i));
    15. printf("%f %c %d\n", t.f, t.c, t.i);
    16. return 0;
    17. }

    打印结果为:

    我们可以通过调试观察arr

    4.4 对比总结

    接下来通过对比来总结printf, scanf, fprintf, fscanf, sprintf, sscanf这六个函数

    printf:格式化输出函数,适用于标准输出流(屏幕)

    scanf:格式化输入函数,适用于标准输入流(屏幕)

    fprintf:格式化输出函数,适用于所有输出流(文件流,标准输出流)

    fscanf:格式化输入函数,适用于所有输入流(文件流,标准输入流)

    sprintf:将格式化数据转化为字符串

    sscanf:将字符串转化为格式化数据


    🙉至此,对文件的第一部分讲解结束,接下来会继续讲解文件相关知识

  • 相关阅读:
    #每日一题合集#牛客JZ13-JZ22
    colab使用(基础入门)——随手记
    Promethus+node_exporter集群部署监控
    解决Oracle报错ORA-01403: 未找到任何数据
    NB6L295M STM32 GD32 IO模拟驱动设计
    Java RPC调用: 远程过程调用的实现与应用
    ssh-keygen命令生成RSA、ed25519类型密钥对
    Android多线程学习:线程池(二)
    egrep扩展正则表达式
    11. 盛最多水的容器
  • 原文地址:https://blog.csdn.net/m0_74270127/article/details/133659571