• 【C语言】文件操作


    C语言的文件操作是可以在文件中存储数据与从文件中引出数据的重要知识,也是程序员必备的知识

    1.为什么使用文件

    比如我们写了通讯录的程序,当通讯录运行起来的时候,可以给通讯录中增加、删除数据,此时数据是存放在内存中,当程序退出的时候,通讯录中的数据自然就不存在了,等下次运行通讯录程序的时候,数据又得重新录入,如果使用这样的通讯录就很难受。
    我们在想既然是通讯录就应该把信息记录下来,只有我们自己选择删除数据的时候,数据才不复存在。
    这就涉及到了数据持久化的问题,我们一般数据持久化的方法有,把数据存放在磁盘文件、存放到数据库等方式。
    使用文件我们可以将数据直接存放在电脑的硬盘上,做到了数据的持久化。

    2.什么是文件

    磁盘上的文件是文件。
    但是在程序设计中,我们一般谈的文件有两种:程序文件数据文件(从文件功能的角度来分类的)。

    2.1程序文件

    包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.obj)。

    2.2数据文件

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

    我们这篇文章讨论的主要就是数据文件

    2.3文件名

    一个文件要有一个唯一的文件标识,以便用户识别和引用。
    文件名包含3部分:文件路径+文件名主干+文件后缀
    例如: c:\code\test.txt
    为了方便起见,文件标识常被称为文件名。

    3.文件的打开和关闭

    3.1文件指针

    缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”。
    每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名FILE.
    例如,VS2013编译环境提供的 stdio.h 头文件中有以下的文件类型申明:

    struct _iobuf {
        char *_ptr;
        int  _cnt;
        char *_base;
        int  _flag;
        int  _file;
        int  _charbuf;
        int  _bufsiz;
        char *_tmpfname;
       };
    typedef struct _iobuf FILE;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异。
    每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,使用者不必关心细节。
    一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。
    下面我们可以创建一个FILE*的指针变量:

    FILE* pf;//文件指针变量
    
    • 1

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

    3.2文件的打开和关闭

    文件在读写之前应该先打开文件,在使用结束之后应该关闭文件。
    在编写程序的时候,在打开文件的同时,都会返回一个FILE*的指针变量指向该文件,也相当于建立了指针和文件的关系。
    ANSIC 规定使用fopen函数来打开文件,fclose来关闭文件。

    //打开文件
    FILE * fopen ( const char * filename, const char * mode );
    //关闭文件
    int fclose ( FILE * stream );
    
    • 1
    • 2
    • 3
    • 4

    文件的打开的方式:
    在这里插入图片描述

    4. 文件的顺序读写

    在这里插入图片描述
    看到这你可能会疑惑,什么是输出流,什么是输入流
    例如:
    输出流至少包括文件流(我们上文提到的FILE*指针变量pf)与标准输出流屏幕

    • 当我们想在文件中写入数据时,把要写的数据写入文件流中就可以

    • 当我们想在屏幕输出数据时,向标准输出流中输入即可

    这时你肯定会说,我们使用printf打印时并没有向标准输出流中输入啊。
    因为我们在打开文件时,会自动打开三个流(包含标准输出流),printf会自动向标准输入流中输入,故会打印在屏幕上:

    标准输出流(stdout),标准输入流(stdin),与标准错误流(stderr

    适用于所有输出流就意味着可以向任意地方输入(光盘,磁盘,屏幕…)

    输入流也是同理

    接下来演示如何使用以上函数:
    想对函数了解更多可以上cplusplus网站查看

    fputc(一次操作一个字符)

    //fputc
    int main()
    {
    	char ch = 'a';
    
    	FILE* pf = fopen("data.txt", "w");
    	if (pf == NULL)
    	{
    		perror("fopen");
    		return 1;
    	}
    	for (int i = 0; i < 10; i++)
    	{
    		fputc(ch, pf);
    		ch++;
    	}
    	fclose(pf);
    	pf = NULL;
    	return 0;
    }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    打开data.txt发现
    在这里插入图片描述

    fgetc(一次操作一个字符)

    //fputc
    int main()
    {
    
    	FILE* pf = fopen("data.txt", "r");
    	if (pf == NULL)
    	{
    		perror("fopen");
    		return 1;
    	}
    	for (int i = 0; i < 10; i++)
    	{
    		int ret = fgetc(pf);
    		printf("%c ", ret);
    	}
    	fclose(pf);
    	pf = NULL;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

    fputs(一次操作一行字符)

    //fputs
    int main()
    {
    	//打开文件
    	FILE* pf = fopen("data.txt", "w");
    	if (pf == NULL)
    	{
    		perror("fopen");
    		return 1;
    	}
    	//操作
    	char str1[] = "hello\n";
    	char str2[] = "world";
    	fputs(str1, pf);
    	fputs(str2, pf);
    	//关闭文件
    	fclose(pf);
    	pf = NULL;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    打开文件:
    在这里插入图片描述

    fgets(一次操作一行字符)

    //fgets
    int main()
    {
    	//打开文件
    	FILE* pf = fopen("data.txt", "r");
    	if (pf == NULL)
    	{
    		perror("fopen");
    		return 1;
    	}
    	//操作
    	char str1[20] = { 0 };
    	fgets(str1, 7, pf);
    	printf("%s", str1);
    
    	fgets(str1, 7, pf);
    	printf("%s\n", str1);
    
    	//关闭文件
    	fclose(pf);
    	pf = NULL;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述

    fprintf(按格式输入函数)

    //fprintf
    struct S
    {
    	int a;
    	char b;
    	float c;
    }s;
    
    int main()
    {
    	//open
    	FILE* pf = fopen("data.txt", "w");
    	if (pf == NULL)
    	{
    		perror("fopen");
    		return 1;
    	}
    	//oper
    	s.a = 100;
    	s.b = 'w';
    	s.c = 3.14f;
    	fprintf(pf, "%d-%c-%f", s.a, s.b, s.c);
    
    	//close
    	fclose(pf);
    	pf = NULL;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    打开文件:
    在这里插入图片描述

    fscanf(按格式输出函数)

    int main()
    {
    	//open
    	FILE* pf = fopen("data.txt", "r");
    	if (pf == NULL)
    	{
    		perror("fopen");
    		return 1;
    	}
    	//oper
    	fscanf(pf, "%d-%c-%f", &(s.a), &(s.b), &(s.c));
    	printf("%d-%c-%f", s.a, s.b, s.c);
    
    	//close
    	fclose(pf);
    	pf = NULL;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在这里插入图片描述

    fwrite(二进制输入)

    int main()
    {
    	int arr[] = { 1,2,3,4,5,6 };
    
    	FILE* pf = fopen("data.txt", "wb");
    	if (pf == NULL)
    	{
    		perror("fopen");
    		return 1;
    	}
    
    	fwrite(arr, 4, 6, pf);
    
    	fclose(pf);
    	pf = NULL;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    打开文件:(用肉眼看不懂)
    在这里插入图片描述

    fread(二进制输出)

    int main()
    {
    	int arr[] = { 1,2,3,4,5,6 };
    
    	FILE* pf = fopen("data.txt", "rb");
    	if (pf == NULL)
    	{
    		perror("fopen");
    		return 1;
    	}
    
    	fread(arr, 4, 10, pf);
    	for (int i = 0; i < 6; i++)
    	{
    		printf("%d ", *(arr + i));
    	}
    
    	fclose(pf);
    	pf = NULL;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    只有机器可以读懂
    在这里插入图片描述

    4.1 sscanf与sprintf

    与printf与scanf相似的还有这两个函数,我们也顺便研究一下
    先看结论:
    在这里插入图片描述

    举例:

    int main()
    {
    	struct S s = { s.a = 100,s.b = 'w',s.c = 3.14f };
    	char str[100] = { 0 };
    
    	sprintf(str, "%d-%c-%f", s.a, s.b, s.c);
    	printf("%s\n", str);
    	
    	struct S tmp = { 0 };
    	sscanf(str, "%d-%c-%f", &(tmp.a), &(tmp.b), &(tmp.c));
    	printf("%d\n%c\n%f\n", s.a, s.b, s.c);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这里插入图片描述

    5.文件的随机读写

    前边我们讲解了顺序读写,那么必然有随机读写,但不是真的随机,而是指哪打哪

    fseek

    (将文件状态指针,类似光标,定到你想要定的位置)
    设文件中有abcdef

    //fseek
    int main()
    {
    	FILE* pf = fopen("data.txt", "r");
    	if (pf == NULL)
    	{
    		perror("fopen");
    		return 1;
    	}
    
    	int ch = fgetc(pf);
    	printf("%c\n", ch);
    	ch = fgetc(pf);
    	printf("%c\n", ch);
    	ch = fgetc(pf);
    	printf("%c\n", ch);
    	fseek(pf, 0, SEEK_SET);
    	ch = fgetc(pf);
    	printf("%c\n", ch);
    
    	fclose(pf);
    	pf = NULL;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述

    ftell

    告诉我们文件状态指针偏移量

    int main()
    {
    	FILE* pf = fopen("data.txt", "r");
    	if (pf == NULL)
    	{
    		perror("fopen");
    		return 1;
    	}
    
    	printf("%c ", getc(pf));
    	printf("%c ", getc(pf));
    	printf("%c ", getc(pf));
    	int ch = ftell(pf);
    	printf("%d\n", ch);
    
    
    	fclose(pf);
    	pf == NULL;
    
    	return 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在这里插入图片描述

    rewind

    将文件状态指针定位在偏移量为0的位置

    int main()
    {
    	FILE* pf = fopen("data.txt", "r");
    	if (pf == NULL)
    	{
    		perror("fopen");
    		return 1;
    	}
    
    	printf("%c ", getc(pf));
    	printf("%c ", getc(pf));
    	printf("%c\n", getc(pf));
    	int ch = ftell(pf);
    	printf("%d ", ch);
    	rewind(pf);
    	ch = ftell(pf);
    	printf("%d\n", ch);
    
    	fclose(pf);
    	pf == NULL;
    
    	return 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述

    6.文件结束判定

    被错误使用的feof

    牢记:在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束。
    而是应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束。

    1. 文本文件读取是否结束,判断返回值是否为 EOFfgetc ),或者 NULLfgets
      例如:
      fgetc 判断是否为 EOF .
      fgets 判断返回值是否为 NULL .
    2. 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。
      例如:
      fread判断返回值是否小于实际要读的个数。

    欢迎讨论

  • 相关阅读:
    HTML期末作业 蛋糕bootstrap响应式网站html+css+javascript+jquery+bootstarp
    IDEA中如何配置多个版本的JDK
    MacBook当作Win电脑副屏
    单机高并发模型设计
    2023.11.14 关于 Spring Boot 创建和使用
    VMware虚拟机的安装教程
    Kamiya丨Kamiya艾美捷人CP ELISA说明书
    AUTOSAR AP 硬核知识点梳理(2)— 架构详解
    【科学文献计量】标准参考出版年谱(Standard RPYS)和多维参考出版年谱(Multi RPYS)
    labview入门到出家11(补充)——基于单片机和labview开发的虚拟示波器
  • 原文地址:https://blog.csdn.net/2301_78636079/article/details/133817984