【C语言】文件的操作

本文详细解释了为何需要使用文件、文件的分类(程序文件和数据文件)、二进制文件和文本文件的区别、流和文件指针的概念,以及文件的打开、关闭、顺序读写、随机读写、结束判定和文件缓冲区的工作原理。通过实例展示了C语言中的相关函数如fopen、fclose、fputc、fgetc等的用法和功能对比。
摘要由CSDN通过智能技术生成

目录

📌📌📌为什们要使用文件

📌📌📌什么是文件

📍程序文件:

📍数据文件:

📍​​​​​​​​​​​文件名

📌📌📌二进制文件和文本文件

📍​​​​​​​二进制文件

📍​​​​​​​​​​文本文件

📌📌📌流

📍​​​​​​​标准流

📍​​​​​​​文件指针

📌📌📌文件的打开和关闭

📌📌📌文件的顺序读写

🚨fputc

🚨fgetc

🚨fgets

🚨fputs

🚨fscanf

🚨fprintf

💕对比两组函数

💕sprintf

💕sscanf

 🚨fwrite

🚨fread

📌📌📌文件的随机读写

🚨fseek

🚨ftell

🚨rewind

 文件读取结束的判定

 🚨feof

  🚨ferror 

📌📌📌文件缓冲区


📌📌📌为什们要使用文件

        我们平常写的代码都是存储在电脑的内存中,一旦程序运行结束,内存回收,数据也跟着丢失,等下次再次运行程序,找不到的上次

的数据,如果想要数据能够持久化的保存,就要使用文件了 

📌📌📌什么是文件

磁盘(硬盘)上的文件是文件

在程序设计中,一般分为两种文件:程序文件数据文件(从文件的功能角度分类)

📍​​​​​​​程序文件:

通俗点就是我们写的代码,程序文件包括


源文件(后缀名.c),

目标文件(windows环境后缀.obj),

可执行文件(windows环境后缀为.exe)


打开我们写的C语言文件都可以看到这些文件

📍​​​​​​​数据文件:

        文件不一定全是我们写的程序,也可能存储数据,而数据文件就是程序运行时需要进行读写的件,比如:程序需要从某个文件中读取

数据,或者是将程序里的数据写到文件的这些文件就被称为数据文件

我们之前的学习大部分都是从终端的键盘上读取数据,输出打印到显示器屏幕上,输入对象是键盘,输出对象是显示器,而现在学的文件

就是将数据存到磁盘的文件中,需要的时候在从磁盘的文件中去读取数据存储到内存中,输入输出对象都是文件

📍​​​​​​​​​​​文件名

每一个文件都有其独特的标签来区分文件即文件名

文件名组成:文件路径 + 文件名主干 + 文件后缀

例如:                c:\code\test.txt
文件路径:         c:\code\
文件主干:                       test
文件后缀名:                          .txt

📌📌📌二进制文件和文本文件

根据文件的组织形式,数据文件可分为二进制文件文本文件

📍​​​​​​​二进制文件

数据以二进制形式在内存中存储,如果不加以转换地直接以二进制的形式存储到文件中,该文件就是二进制文件

📍​​​​​​​​​​文本文件

如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件
一个数据在文件中是怎么存储的呢? 字符一律以ASCII形式存储 数值型数据 既可以用 ASCII形式 存储,也可以使用 二进制形式 存储。 如有
整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符⼀个字节),而二进制形式输出,则在磁盘上只占4个字
节(VS2019测试)

现在已经把10000以二进制形式存储到test文档中了,如果我们在磁盘上以记事本打开这个文档,发现我们啥也看不懂,因为10000是以二进制存储进去,而我们利用文本编译器打开,他是看不懂的,所以就打印出乱码

不过我们可以利用VS来查看,具体操作步骤如下: 

 

 上述的test文件就是二进制文件

想这种就是文本文件

📌📌📌

我们的程序有时要从各种各样的外部设备读取数据,有时需要将数据输出到各种外部设备上,而不同的设备对应的操作方法各有不同,为了方便程序员操作,抽象出流的概念,流可以理解为流淌着字符的河流

程序员只需要关心怎么把从流中读取数据及如何将数据输出到流中 ,其他操作有C语言的底层逻辑实现,这样大大简化了编程难度

C程序针对文件、画面、键盘等的数据输入输出操作都是通过流操作的
一般情况下,我们要想向流里写数据,或者 从流中读取数据 ,都是要打开流,然后操作

📍​​​​​​​标准流

那为什么我们从键盘输入数据,向屏幕上输出数据,并没有打开流呢?
那是因为C语言程序在启动的时候,默认打开了3个流:
  • stdin - 标准输入流,在大多数的环境中从键盘输入,scanf函数就是从标准输入流中读取数据(键盘)
  • stdout - 标准输出流,大多数的环境中输出值至显示器界面,printf函数就是将信息输出到标准输出流中(屏幕)
  • stderr - 标准错误流,大多数环境中输出到显示器界面(屏幕)
这是默认打开了这三个流,我们使用scanf、printf等函数就可以直接进行输入输出操作的
stdin、stdout、stderr 三个流的类型是: FILE* ,通常称为文件指针
C语言中,就是通过 FILE* 的文件指针来维护流的各种操作的

📍​​​​​​​文件指针

每个打开的文件都会在内存中开辟一块文件信息区,用来存储文件的相关信息(文件名字,文件状态,文件当前位置等),这些信息存储在一

个结构体变量中,该结构体类型是由系统声明的,取名 FILE

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

每次打开文件,系统会根据文件的情况自动创建一个FILE结构的变量,之后系统会自动填充文件相关信息

一般都是通过⼀个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便
下面我们可以创建一个FILE*的指针变量
FILE* pf//文件指针

定义pf是一个指向文件信息区(一个FILE类型的结构体)的文件指针,通过pf文件指针就可以访问结构体里面的成员,从而间接找到该文件,

即文件指针能够间接地找到与之关联的文件

📌📌📌文件的打开和关闭

文件再进行读写前,应该先打开文件,结束使用后在关闭文件

ANSIC 规定使用  fopen 函数来 打开文件 fclose 关闭文件
//打开⽂件
FILE * fopen ( const char * filename, const char * mode );

//关闭⽂件
int fclose ( FILE * stream );

举例:
//打开⽂件
FILE * pf  = fopen( "test.txt", "w" );

//关闭⽂件
fclose ( "pf" );
  • fopen:文件指针,存储返回的文件信息区的地址
  • filename:文件名
  • mode:文件打开模式
  • stream:与要关闭的文件相关的文件指针

 以下是文件的各种打开模式

📌📌📌文件的顺序读写

🚨fputc

  • 将字符写到文档中的函数
  • c 是输出到文件的字符
  • stream 是文件指针
  • 每写入一个字符到文档中,光标(位置指示器)往后 + 1
  • 成功后,将返回所写字符
  • 如果发生写入错误,则返回 EOF 并设置错误指示器 (ferror)为0

使用

🚨fgetc

  •  每次从文档中往后读取一个字符
  • stream 是要读取的文档与之相关的文件指针
  • 读取成功后,将返回读取的字符(提升为 int 值),光标(位置指示器)也将往后 + 1
  • 返回类型为 int 以适应特殊值 EOF,该值指示失败
  • 如果位置指示器位于文件末尾,则函数返回 EOF 并设置的 eof 指示符 (feof
    如果发生其他读取错误,该函数也会返回 EOF,但会设置其错误指示器 (ferror

使用:

🚨fgets

  •  从文档中读取字符串到数组中
  • string 为目标空间首地址
  • 返回值返回目标空间首地址
  • 中读取字符,并将它们作为 C 字符串存储到 string 中,直到读取 num-1 个字符或到达换行符或文件末尾,以先发生者为准
  • 换行符使 fgets 停止读取,但它被函数视为有效字符,并包含在复制到 str 的字符串中
  • 成功后,该函数返回 string
  • 如果发生读取错误,则设置错误指示符 (ferror)为0,并返回 null 指针

第一次读取文档中的第一行时,因为第一行字符个数大于实际读取个数9,所以第一次读取会先读9个字符,等第二次读取时,从第9个往后继续读取4个h,之后遇到换行符第二次读取结束,往后的读取都是先遇到换行符读取结束

🚨fputs

  • 将 string指向的字符串复制到文档中
  • 该函数从指定的地址 string 开始复制,直到到达终止 null 字符 ' \0 ' 
  • 成功后,将返回一个非负值
    出错时,该函数返回 EOF 并设置错误指示器 为0

使用:

如果想要在文档 中换行,应在要传的字符串末尾 + 换行符

🚨fscanf

  • 从指定的流中读取格式化的数据

我们发现fscanf函数与我们之前学的scanf 相似;所以我们可以对比一下两个函数的参数

可以发现,fscanf 函数就比scanf 函数多了一个参数,文件流 stream,所以理论上会用scanf 函数就会用 scanf 函数

使用:

🚨fprintf

  • 把数据以格式化的形式打印到指定的流上

对比一下fprintf 和 printf 两个函数的参数

fprintf 函数比 printf 函数多了一个参数,所以用法大致与printf 函数一样

使用:


 还有要注意fscanf 和fprintf 函数适用于所有流,包括文件流及标准流,不仅限于文件流

在前一个用例基础上,我们把 printf 改成fprintf 来进行打印,照样可以打印到屏幕上

只需改变参数stream ,指定要具体打印到哪一个流上,sscanf 也是一样

💕对比两组函数

scanf/fscanf/sscanf

printf/fprintf/sprintf

💕sprintf

  • 将格式化数据写入字符串buffer
  • 用法与printf/fprintf用法类似

💕sscanf

  •  从字符串中格式化读取数据
  •  用法与scanf/fscanf用法类似

 使用:


 所有根据以上所学到的函数可以总结出以下几点

  • scanf:从标准输入流上读取格式化数据
  • fscanf:从指定的输入流上读取格式化数据
  • sscanf:从字符串中读取格式化数据

  • printf:把数据以格式化的形式打印到标准输出流
  • fprintf:把数据以格式化的形式打印到指定输出流
  • sprintf :把数据以格式化的形式打印到字符串

上面讲到的函数针对的都是文本

而下面讲的函数针对的是二进制 

 🚨fwrite

  •  从buffer指向的数组中复制count 个 size个字节大小的内容到指定的文件流中
  • count 为元素个数
  • size 为每个元素的大小

使用: 

现在再以二进制的形式读取数据 

🚨fread

  • 从指定的文件流中读取count 个 大小为size 的字节内容复制到字符串buffer里面
  • count 为元素个数
  • size 为每个元素的大小

使用:

文档里面依旧存的是刚才的二进制内容,现在以二进制的方式读出来

📌📌📌文件的随机读写

🚨fseek

  • 第一个参数stream是对应的文件流
  • 第二个参数offset是相对于第三个参数origin的偏移量,向左偏移为负数,向右偏移为正数
  • 第三个参数origin是第二个参数偏移量所参考的起始位置,有以下三个选项
                            选项                                                              起始偏移位置
                      SEEK_SET                                                            文件的开始位置
                      SEEK_CUR                                                     文件指针当前指向的位置
                      SEEK_END                                                                 文件的结尾

使用: 


🚨ftell

返回文件指针相对于起始位置的偏移量

🚨rewind

让文件指针的位置回到文件的起始位置

 文件读取结束的判定

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

 🚨feof

作用:当文件读取结束的时候,判断是读取结束的原因是否是:遇到文件末尾结束

如果返回一个非零的值说明文件是正常读取遇到了文件结束标志而结束的

  🚨ferror 

作用:当文件读取结束的时候,判断是读取结束的原因是否是:读取是否发生错误 

检查错误指示器,如果返回一个非零的值说明是在文件读取过程中出错而导致文件读取结束的

打开一个流时,这个流上面有两个标记值:

  1. 是否遇到文件末尾  ---- feof
  2. 读取是否发生错误  ---- ferror

那个标记值发生改变,文件读取结束的原因与之相关

 使用:

#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);
}

📌📌📌文件缓冲区

        ANSIC 标准采用“缓冲文件系统” 处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。 从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上 如果从磁盘向计算机读入数据,则 从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区 (程序变量等)。缓冲区的大小根据C编译系统决定的

 💕💕💕今天就介绍到这,如有错误,望大佬矫正!制作不易,望三连支持一下💕💕💕

  • 30
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

hao xiao zi

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

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

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

打赏作者

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

抵扣说明:

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

余额充值