【C语言】0基础教程——文件操作

本文详细介绍了C语言中文件的基本概念和操作,包括文件的分类、文件指针、文件打开与关闭、读写函数如fopen、fclose、fputc、fgetc、fprintf等,并探讨了文本文件与二进制文件的区别。文章通过实例代码展示了文件操作的常见用法,帮助读者理解如何在C语言中进行文件管理和数据持久化。
摘要由CSDN通过智能技术生成

目录

🍁为什么要使用文件

🍁什么是文件

😶‍🌫️文件

1.程序文件

2.数据文件

我们写代码的时候与文件是什么关系呢?

😶‍🌫️文件名

😶‍🌫️文件指针

文件信息区那这块内存在什么位置呢?

由什么来维护呢?

🍁相关函数

😶‍🌫️文件的打开和关闭(fopen,fclose)

文件有那些打开方式呢?

        例一:只写

        例二:只读

😶‍🌫️文件的顺序读写

1.fputc

        例一

2. fgetc

         例二

3.fguts

        例三

4.fgets

        例四

5.fprintf

        例五

6.fscanf

        例6

7.fwrite

        例7

8.fread

        例子8

9.fseek

        例9

10.rewind

        例10

11.feof

        例11

🍁文本文件和二进制文件

数据在文本文件中和在二进制文件中分别是怎么存储的?

🍁面试题



🍁为什么要使用文件

        在上一篇文章里展示了如何构造一个通讯录,可是一旦程序退出后,空间便还给操作系统,不存在了,但是当我我们需要保留下这些文件时又无可奈何,所以这章通过学习文件操作,便可以实现保存信息功能啦。


🍁什么是文件

😶‍🌫️文件

磁盘上的文件即是文件,但我们从文件功能的角度看又往往分为两类

1.程序文件

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

2.数据文件

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

我们写代码的时候与文件是什么关系呢?

看下图

        可能有人又不明白啦,为什么写是输出到文件,读是输入到内存里呢?

首先,可以先问问自己,真的知道什么内存吗?(博主经常听到有人说自己手机内存256G...一提运行内存就说8G)

        比如,你买了个手机,是8G/256G的,这两个数据分别是什么呢?实际上前面那个数值小一点的叫内存,也就是咱平时口中的运行内存,,而后面那个较大的数值呢,叫外存,也就是咱平时说的什么C盘D盘...这些磁盘。

       理解了这些,再结合上图,就不难理解,从文件中读入数据会存入内存的意思了(实际上就是输入缓冲区)。

😶‍🌫️文件名

一个文件要有一个唯一的文件标识,以便用户识别和引用。

文件名包含3部分:文件路径+文件名主干+文件后缀

例如:c:\code\test.txt

有些人可能会问,为什么我没有看见文件后缀名呢?

那不妨看看下图这个选项打开了吗?

为了方便起见,文件标识常被称为文件名。

😶‍🌫️文件指针

缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”

每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。

这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名FILE.

听着是不是有点懵?什么意思呢?

        通俗的讲啊,这个文件指针是用来维护一块空间的,怎样的一块空间呢?是一个结构体,在stdio.h定义中被声明为FILE

看以下图解:

假设我的磁盘中有一个文件叫test.txt这样一个文件,如下图:

 一旦当我打开这个文件时,他就会在内存中开辟一块文件信息区(一个结构体,类型是FILE),而FILE ptr(假设创建的变量名为ptr)这个结构体就是这个文件信息区的一块空间(如下图)

 想看这个结构体的成员,可以通过VS2013,(2019已经不显示了)如下:

struct _iobuf 
{       char *_ptr;
        int   _cnt;
        char *_base; 
        int   _flag;
        int   _file;
        int   _charbuf;
        int   _bufsiz;
        char *_tmpfname;
};typedef struct _iobuf FILE;

刚刚提到一旦打开test.c文件就会在内存中创建文件信息区

文件信息区那这块内存在什么位置呢?

由什么来维护呢?

        在C语言的程序里通过fopen这个函数可以打开文件,打开成功会后就会返回这块空间起始位置的地址,这块空间类型为FILE,所以返回的地址就是FILE*类型,程序员就可以通过创建一个FILE*的变量来维护这块空间,这就是文件指针。

下面演示一下如何创建一个指针变量

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

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

如下图:


🍁相关函数

😶‍🌫️文件的打开和关闭(fopen,fclose)

咱先给出语法格式

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

需要注意以下几点:

        文件读写前要打开文件,读写后要关闭文件;

        ANSIC 规定使用fopen函数来打开文件,fclose来关闭文件;

        在编写文件,打开文件的同时fopen会返回一个指针,若打开成功,则返回该文件在文件信息区的起始空间地址,若打开失败则返回空指针,所以我们为了防止程序出bug,因该这样写(如下代码):

#include<stdio.h>
#include<errno.h>
#include<string.h>

int main()
{
	FILE* ptr = fopen("test.txt", "w");
	if (ptr == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	//处理
	fclose(ptr);
	return 0;
}

亦或是这样写,如下(报出错误信息即可)

#include<stdio.h>
#include<errno.h>
#include<string.h>

int main()
{
	FILE* ptr = fopen("test.txt", "w");
	if (ptr == NULL)
	{
		perror("fopen");//如果没能打开文件,perror会直接打印错误信息
		return 1;       //括号里是可以任意写的一个常量字符串
	}
	//处理
	fclose(ptr);
	return 0;
}

这样写真的就够了吗?

        想象一下,fclose可以释放带哦ptr所指向的那块空间,而一旦ptr所指向的空间被释放掉,ptr便成了野指针,所以将ptr置为空指针,如下:

#include<stdio.h>
#include<errno.h>
#include<string.h>

int main()
{
	FILE* ptr = fopen("test.txt", "w");
	if (ptr == NULL)
	{
		perror("fopen");//如果没能打开文件,perror会直接打印错误信息
		return 1;       //括号里是可以任意写的一个常量字符串
	}
	//处理
	fclose(ptr);
    ptr = NULL;
	return 0;
}

文件有那些打开方式呢?

 最常用的便是只读、只写,以下为代码演示:

例一:只写

#include<stdio.h>
#include<errno.h>
#include<string.h>

int main()
{

	//打开文件
	FILE* ptr = fopen("test.txt", "w");
	if (ptr == NULL)
	{
		perror("fopen");//如果没能打开文件,perror会直接打印错误信息
		return 1;       //括号里是可以任意写的一个常量字符串
	}

	//处理
	fprintf(ptr, "hello world");

	//关闭文件
	fclose(ptr);
	return 0;
}

例二:只读

#include<stdio.h>
#include<errno.h>
#include<string.h>

int main()
{
	char str[15] = { 0 };
	//打开文件
	FILE* ptr = fopen("test.txt", "w");
	if (ptr == NULL)
	{
		perror("ptr");//如果没能打开文件,perror会直接打印错误信息
		return 1;       //括号里是可以任意写的一个常量字符串
	}

	//处理
	fprintf(ptr, "hello world");

	//关闭文件
	fclose(ptr);

	//打开文件
	FILE* ptr_ = fopen("test.txt", "r");
	if (ptr_ == NULL)
	{
		perror("ptr_");
		return 1;
	}

	//处理
	fgets(str, sizeof(str), ptr_);
	printf("%s\n", str);

	//关闭文件
	fclose(ptr_);
	ptr_ = NULL;

	return 0;
}

😶‍🌫️文件的顺序读写

1.fputc

先看看要求

怎么用呢?

格式:

int fputc( int c, FILE *stream );

        c表示要输入的字符,stream表示要被输出的位置

例一

#include<stdio.h>
#include<errno.h>
#include<string.h>

int main()
{
	//打开文件
	FILE* ptr = fopen("test.txt", "w");
	if (ptr == NULL)
	{
		perror("ptr");//如果没能打开文件,perror会直接打印错误信息
		return 1;       //括号里是可以任意写的一个常量字符串
	}

	//处理
	char i = 0;
	for (i = 'a'; i <= 'z'; i++)
	{
		fputc(i, ptr);
	}

	//关闭文件
	fclose(ptr);
	ptr = NULL;
	
	return 0;
}

2. fgetc

先看看要求

 

 怎么用呢?

格式:

        

int fgetc( FILE *stream );

        FILE* stream 表示一个文件指针变量,stream表示流,也就是介于内存和文件信息区的输入缓存区(这里不细讲,不是本篇重点,以后会出相关博客),简而言之就是需要你刚刚打开的文件的指针,返回类型为int。

 例二

#include<stdio.h>
#include<errno.h>
#include<string.h>

int main()
{
	//打开文件
	FILE* ptr = fopen("test.txt", "w");
	if (ptr == NULL)
	{
		perror("ptr");//如果没能打开文件,perror会直接打印错误信息
		return 1;       //括号里是可以任意写的一个常量字符串
	}

	//处理
	char i = 0;
	for (i = 'a'; i <= 'z'; i++)
	{
		fputc(i, ptr);
	}

	//关闭文件
	fclose(ptr);
	ptr = NULL;
	
	//打开文件
	FILE* ptr_ = fopen("test.txt", "r");
	if (ptr_ == NULL)
	{
		perror("ptr_");
		return 1;
	}

	//处理
	int ch = 0;//要注意,fgetc返回值是int类型,所以用int来接收
	while ((ch = fgetc(ptr_)) != EOF)//读取完返回EOF  这里注意优先级,用括号括起来
	{
		printf("%c ", ch);
	}


	//关闭文件
	fclose(ptr_);
	ptr_ = NULL;

	return 0;
}

3.fguts

先来看看要求

 怎么用呢?

格式

int fputs( const char *string, FILE *stream );

         string是要被输出的常量字符串,stream是被输出的文件指针;

例三

#include<stdio.h>
#include<errno.h>
#include<string.h>

int main()
{
	//打开文件
	FILE* ptr = fopen("test.txt", "w");
	if (ptr == NULL)
	{
		perror("ptr");
		return 1;
	}

	//操作
	fputs("hehe\n", ptr);

	//关闭文件
	fclose(ptr);
	ptr = NULL;
	return 0;
}

4.fgets

先来看看要求

怎么用呢?

格式

char *fgets( char *string, int n, FILE *stream );

        从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。

例四

#include<stdio.h>
#include<errno.h>
#include<string.h>

int main()
{
	//打开文件
	FILE* ptr = fopen("test.txt", "w");
	if (ptr == NULL)
	{
		perror("ptr");
		return 1;
	}

	//操作
	fputs("hehe\n", ptr);

	//关闭文件
	fclose(ptr);
	ptr = NULL;

	//打开文件
	FILE* p = fopen("test.txt", "r");
	if (p == NULL)
	{
		perror("p");
		return 1;
	}

	//操作
	char str[5] = { 0 };
	fgets(str, 5, p);
	printf("%s\n", str);

	//关闭文件
	fclose(p);
	p = NULL;
	return 0;
}

5.fprintf

先看看要求

 怎么用呢?

格式

int fprintf( FILE *stream, const char *format [, argument ]...);

 这里都可以对比printf的格式

int printf(const char *format [, argument ]...);

实际不难看懂,只是多了一个被输出的文件流

例五

#include<stdio.h>
#include<errno.h>
#include<string.h>

int main()
{
	//打开文件
	FILE* ptr = fopen("test.txt", "w");
	if (ptr == NULL)
	{
		perror("ptr");
		return 1;
	}

	//操作文件
	fprintf(ptr, "hello world\n");

	//关闭文件
	fclose(ptr);
	ptr = NULL;

	return 0;
}

6.fscanf

先来看看要求

 怎么用呢?

格式

int fscanf( FILE *stream, const char *format [, argument ]... );

        同理和scanf函数对比格式

int scanf(const char *format [, argument ]... );

不难理解,也是少了文件指针流

例6

#include<stdio.h>
#include<errno.h>
#include<string.h>

int main()
{
	//打开文件
	FILE* ptr = fopen("test.txt", "w");
	if (ptr == NULL)
	{
		perror("ptr");
		return 1;
	}

	//操作文件
	fprintf(ptr, "hello world\n");

	//关闭文件
	fclose(ptr);
	ptr = NULL;

	//打开文件
	FILE* p = fopen("test.txt", "r");
	if (p == NULL)
	{
		perror("p");
		return 1;
	}

	//操作文件
	char str[20] = { 0 };
	fscanf(p, "%s", str);//fscanf遇到空格停止读取
	printf("%s ", str);
	fscanf(p, "%s", str);
	printf("%s\n", str);

	//关闭文件
	fclose(p);
	p = NULL;

	return 0;
}

7.fwrite

先看看要求

 怎么用呢?

 格式

size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );

        简而言之:将buffer这个任意类型,放入count个以size个字节的大小到stream

例7

int main()
{
	//打开文件
	FILE* ptr = fopen("test.txt", "w");
	if (ptr == NULL)
	{
		perror("ptr");
		return 1;
	}

	//操作文件
	student s = { "chen",19 };
	fwrite(&s, sizeof(s), 1, ptr);

	//关闭文件
	fclose(ptr);
	ptr = NULL;

	return 0;
}

8.fread

先来看看要求

怎么用呢?

格式

size_t fread( void *buffer, size_t size, size_t count, FILE *stream );

简而言之:将stream这个文件流的count个size大小的数据写入buffer中

例子8

#include<stdio.h>
#include<errno.h>
#include<string.h>

typedef struct student
{
	char name[15];
	int age;
}student;

int main()
{
	//打开文件
	FILE* ptr = fopen("test.txt", "w");
	if (ptr == NULL)
	{
		perror("ptr");
		return 1;
	}

	//操作文件
	student s = { "chen",19 };
	fwrite(&s, sizeof(s), 1, ptr);

	//关闭文件
	fclose(ptr);
	ptr = NULL;

		//打开文件
	FILE* p = fopen("test.txt", "r");
	if (p == NULL)
	{
		perror("p");
		return 1;
	}

	//操作文件
	student s1 = { 0 };
	fread(&s1, sizeof(s), 1, p);
	printf("%s %d\n", s1.name, s1.age);

	//关闭文件
	fclose(p);
	p = NULL;
	return 0;
}

9.fseek

先看看要求

 怎么用呢?

格式

int fseek( FILE *stream, long offset, int origin );

        简而言之:在文件stream里,以为起点origin(偏移起始位置:文件头0(SEEK_SET),当前位置1(SEEK_CUR),文件尾2(SEEK_END),将光标偏移offset个字符;

例9

#include<stdio.h>
#include<errno.h>
#include<string.h>

int main()
{
	//打开文件
	FILE* ptr = fopen("test.txt", "w");
	if (ptr == NULL)
	{
		perror(ptr);
		return 0;
	}

	//处理文件
	fputs("hellowcdef", ptr);
	fseek(ptr, 6, SEEK_SET);
	fputs(" hello world\n", ptr);
	
	//关闭文件
	fclose(ptr);
	ptr = NULL;
	return 0;
}

10.rewind

先看看要求

 怎么用呢?

格式 

//未保存数据丢失//

例10

#include<stdio.h>
#include<errno.h>
#include<string.h>

int main()
{
	//打开文件
	FILE* ptr = fopen("test.txt", "w");
	if (ptr == NULL)
	{
		perror(ptr);
		return 0;
	}

	//处理文件
	fputs("world", ptr);
	rewind(ptr);
	fputs("hello\n", ptr);
	
	//关闭文件
	fclose(ptr);
	ptr = NULL;
	return 0;
}

11.feof

        被许多人误用的函数:feof用来判断文件是否结束

        实际上:feof是在文件结束的时候判断文件结束的原因,判断文件是因为遇到文件位结束还是读取失败结束

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

例11

#include <stdio.h>#include <stdlib.h>
 
int main(void)
{
    int c; // 注意:int,非char,要求处理EOF
    FILE* fp = fopen("test.txt", "r");
    if(!fp)
   {
        perror("File opening failed");
        return EXIT_FAILURE;
   } //fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF
    while ((c = fgetc(fp)) != EOF) // 标准C I/O读取文件循环
   {
        putchar(c);
   }
//判断是什么原因结束的
    if (ferror(fp))
        puts("I/O error when reading");
    else if (feof(fp))
        puts("End of file reached successfully");
     fclose(fp);
     fp = NULL;
     return 0;
}

🍁文本文件和二进制文件

  • 文本文件
  • { 以ASCII字符的形式存储的文件就是文本文件 };
  • 二进制文件
  • 数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件。 }

数据在文本文件中和在二进制文件中分别是怎么存储的?

        如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节),而二进制形式输出,则在磁盘上只占4个字节。


🍁面试题


对比一组函数
scanf/fscanf/sscanf
printf/fprintf/sprintf

scanf/prinf 是针对标准输入流/标准输出流的 格式化输入/输出
fscanf/fprintf 是针对所有输入流/所有输出流的 格式化输入/输出
sscanf/sprintf sscanf是从字符串中读取格式化的数据 sprintf是把格式化的数据输出成(存储到)字符串

码字不易~

  • 10
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈亦康

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值