c语言终点站--文件操作

news2024/11/15 22:32:49

前言:

为什么要学习文件操作呢?想要知道这个问题,我们就需要先了解什么是数据的可持久化。

那么什么是数据的可持久化呢?数据的可持久化就是把内存中的数据对象永久的保存在电脑的磁盘文件中,将程序数据在持久状态和瞬时状态相互转换的机制。

而文件操作就可以让我们达到这一目的。

1.什么是文件

磁盘上的文件都是文件。

在程序设计中,我们将文件分为两种:程序文件、数据文件(从文件功能角度来分类)。

1.1程序文件

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

像我们写代码时创建的test.c或者test.cpp文件就是程序文件。

1.2 数据文件

数据文件存放程序在运行时需要读取的数据。

列如通讯录中的各个对象的数据,在使用通讯录程序时我们需要先从数据文件中读取数据。

接下来我们主要讨论的是数据文件。

在我们日常写代码刷题,处理数据的输入输出都是终端作为对象,即从终端的键盘输入数据,运行结果显示到显示器上。        

就像这个:

其实更多时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使用,这里处理 的就是磁盘上文件。

1.3 文件名

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

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

比如:

 c:\code\test.txt

 这个路径的意思就是c盘下的code文件夹里面的test.txt文件

为了方便查找,文件标识常被称为文件名。 而且在同一目录下的文件名必须不一样。

2.文件的打开与关闭

2.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;

注意,不同的编译器FILE类型包含的内容不完全相同,但是大同小异。

每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息, 使用者不必关心细节。

一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。

下面我们可以创建一个FILE*的指针变量:

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

2.2文件的打开与关闭

要想将数据写入文件,或者是将文件中的数据读出来,都需要先打开目标文件。就像我们如果想喝牛奶,那肯定是先把瓶盖拧开。同样的,在完成一些列操作后,我们需要把文件关闭,跟喝完牛奶要盖上瓶盖一样的道理。

在打开文件的时候,会返回一个FILE类型的指针来指向该文件,我们就通过这个指针来对文件进行数据的读取和写入。

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

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

当我们使用fopen来打开文件的时候,需要传入的参数有两个,一个是文件路径filename,还有一个是打开文件的方式mode.

文件路径怎么书写?

在C语言中,文件路径的书写是根据操作系统的不同而有所差异。下面是一些常用操作系统的文件路径书写方式示例:

  • 绝对路径:C:\folder\file.c
  • 相对路径:folder\file.c 或者 …\folder\file.c (… 表示上一级目录)
绝对路径举例:
 c:\code\test.txt
相对路径举例:
..\\code\\test.txt

这里为什么要用“\\”而不是“\”呢?这就是涉及到转移字符的问题了,这里就不多说了。

有哪些打开方式呢?

文件使用方式含义如果指定文件不存在
“r”(只读)为了输入数据,打开一个已经存在的文本文件出错
“w”(只写)        为了输出数据,打开一个文本文件建立一个新的文件
“a”(追加)向文本文件尾添加数据建立一个新的文件
“rb”(只读)为了输入数据,打开一个二进制文件出错
“wb”(只写)为了输出数据,打开一个二进制文件建立一个新的文件
“ab”(追加)向一个二进制文件尾添加数据出错
“r+”(读写)为了读和写,打开一个文本文件出错
“w+”(读写)为了读和写,建议一个新的文件建立一个新的文件
“a+”(读写)打开一个文件,在文件尾进行读写建立一个新的文件         
“rb+”(读写)为了读和写打开一个二进制文件出错
“wb+”(读写)为了读和写,新建一个新的二进制文件建立一个新的文件         
“ab+”(读写)打开一个二进制文件,在文件尾进行读和写建立一个新的文件         

实例:

#include <stdio.h>
int main ()
{
  FILE * pFile;
  //打开文件
  pFile = fopen ("myfile.txt","w");
  //文件操作
  if (pFile!=NULL)
 {
    fputs ("fopen example",pFile);
    //关闭文件
    fclose (pFile);
 }
  return 0;
}

 3.文件的顺序读写

功能函数名适用于
字符输入函数fgetc所有输入流
字符输出函数fputc所有输出流
文本行输入函数fgets所有输入流
文本行输出函数fputs所有输出流
格式化输入函数fscanf所有输入流
格式化输出函数fprintf所有输出流
二进制输入函数fread文件
二进制输出函数fwrite文件

3.1scanf/fscanf/sscanf的区别:

3.1.1 scanf

标准输入(即键盘)读取输入数据。它的函数原型为int scanf(const char *format, ...),其中format是格式字符串,用于指定输入数据的格式。scanf根据格式字符串解析输入数据,并将解析结果存储到对应的参数中。

3.1.2 fscanf

文件中读取输入数据。它的函数原型为int fscanf(FILE *stream, const char *format, ...),其中stream是指向文件的指针,format是格式字符串。fscanf根据格式字符串从文件中解析输入数据,并将解析结果存储到对应的参数中。

3.1.3 sscanf

字符串中读取输入数据。它的函数原型为int sscanf(const char *str, const char *format, ...),其中str是输入字符串,format是格式字符串。sscanf根据格式字符串从字符串中解析输入数据,并将解析结果存储到对应的参数中。

区别:

这些函数在功能上是类似的,但是适用的输入源不同。scanf适用于从标准输入(键盘)读取数据,fscanf适用于从文件读取数据,sscanf适用于从字符串读取数据。

需要注意的是,这些函数都可以返回成功匹配并读取的参数个数,用于判断读取是否成功。返回值为EOF(-1)表示读取失败。此外,它们的格式字符串的语法是相同的,都可以使用格式控制符来指定输入数据的类型和格式。

printf/fprintf/sprintf的区别跟上面的一样,只不过是输出数据。

4.文件的随机读写

4.1 fseek

根据文件指针的位置和偏移量来定位文件指针.

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

举例:

#include <stdio.h>
int main()
{
	FILE* pFile;
	pFile = fopen("example.txt", "wb");
	fputs("This is an apple.", pFile);
	fseek(pFile, 9, SEEK_SET);
	fputs(" sam", pFile);
	fclose(pFile);
	return 0;
}

打开example.txt文件

我们发现“sam"被插入到了字符串"This is an apple."中第九个位置。

具体是怎么实现的呢?

来看这一行代码

fseek(pFile, 9, SEEK_SET);
  • pFile 是指向文件的指针;
  • 9 是 offset 参数,表示要移动的字节数或记录数;
  • SEEK_SET 是 origin 参数,用于指定相对位置。SEEK_SET 表示从文件的开头开始计算偏移量。

我们通过这一行代码将文件指针pFile的位置向后移动了9个位置,所以我们在接下来的写入操作时,是从第9个位置开始写入的。

4.2 ftell

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

long int ftell ( FILE * stream );

举例:

int main()
{
    FILE* pFile;
    long size;
    pFile = fopen("example.txt", "rb");
    if (pFile == NULL) perror("Error opening file");
    else
    {
        fseek(pFile, 0, SEEK_END);   // non-portable
        size = ftell(pFile);
        fclose(pFile);
        printf("Size of example.txt: %ld bytes.\n", size);
    }
    return 0;
}

这里我们先用fseek将文件指针移动到数据字节的最后一个位置,这个时候ftell返回的就是起始位置到末位置的距离。

 4.3 rewind

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

void rewind ( FILE * stream );

举例:


int main() {
	FILE* pf = fopen("example.txt", "w+");//读和写文件
	char arr[27] = { 0 };
	for (char i = 'a'; i <= 'z'; i++) {
		fputc(i, pf);
	}
	//将26个字母写入到文件中
	//此时文件指针的位置指向最后一个位置
	//输出此时文件指针偏移量
	int x = ftell(pf);
	printf("此时文件指针跟起始位置的偏移量为%d\n", x);
	rewind(pf);//将文件指针的位置移到起始位置
	fread(arr, 1, 26, pf);//将文件里的数据读入到arr中
	for (int i = 0; i < 27; i++) {
		printf("%c ", arr[i]);
	}
	fclose(pf);
	pf = NULL;
	return 0;
}

我们可以看到,通过frewind将文件指针移动到起始位置,我们才能从头开始将26个字母读入到字符数组arr中。

5.文本文件和二进制文件

5.1二进制文件

我们知道,在内存中所有数据都是以二进制的形式储存的。

如果不加任何转换就将其输入到文件中,那这种文件就是二进制文件,而这种文件里的数据我们通常是看不懂的。

举例:

int main() {
	FILE* pf = fopen("example.txt", "wb+");//二进制文件的读和写
	int a=10000;
	fwrite(&a, 4, 1, pf);

	fclose(pf);
	pf = NULL;
	return 0;
}

 

是不是看不懂?

用二进制编译器打开一看

10 27 00 00这是表示的是啥意思?

其实这就是二进制中的10000,只不过是用16进制表示出来了

5.2 文本文件?

求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文 本文件。

用以上例子在文本文件中又是怎么表示的呢?

10000在文本文件中就是五个字符排列在一起的,所以10000的ASCII码的形式存储形式是

 6.文件读取结束的判定

6.1被错误使用的feof

feof函数是C语言中的一个库函数,用于检测文件流的结束标志。它的作用是判断文件是否已经达到文件末尾。当文件读取到末尾时,feof函数返回非零值(真),否则返回零值(假)。

注意:

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

1. 文本文件读取是否结束,判断返回值是否为EOF(fgetc),或者NULL(fgets)。

2. 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。

正确使用:

1.文本文件中:
#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);
}
2.二进制文件中:
#include <stdio.h>
enum { SIZE = 5 };
int main(void)
{
    double a[SIZE] = {1.,2.,3.,4.,5.};
    FILE *fp = fopen("test.bin", "wb"); // 必须用二进制模式
    fwrite(a, sizeof *a, SIZE, fp); // 写 double 的数组
    fclose(fp);
    double b[SIZE];
    fp = fopen("test.bin","rb");
    size_t ret_code = fread(b, sizeof *b, SIZE, fp); // 读 double 的数组
    if(ret_code == SIZE) {//判断成功读取的的数据次数是否等于SIZE
        puts("Array read successfully, contents: ");
        for(int n = 0; n < SIZE; ++n) printf("%f ", b[n]);
        putchar('\n');
   } else { // 不等于说明读取失败了,下面排查失败原因
       if (feof(fp))//读取到了末尾
          printf("Error reading test.bin: unexpected end of file\n");
       else if (ferror(fp)) {//没有读取到末尾
           perror("Error reading test.bin");
       }
   }
    fclose(fp);
    return 0;
}

 7.文件缓冲区

缓冲区概念:

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

同时,缓冲区根据其对应的是输入设备还是输出设备,分为输入缓冲区和输出缓冲区。

为什么要引入缓冲区?

比如我们从磁盘里取信息,我们先把读出的数据放在缓冲区,计算机再直接从缓冲区中取数据,等缓冲区的数据取完后再去磁盘中读取,这样就可以减少磁盘的读写次数,再加上计算机对缓冲区的操作大大快于对磁盘的操作,故应用缓冲区可大大提高计算机的运行速度。

总而言之,缓冲区使得低速的输入输出设备和高速的CPU能够协调工作,避免低速的输入输出设备占用CPU,解放出CPU,使其能够高效率工作。

这里再介绍一个刷新缓冲区的函数fflush:

int fflush ( FILE * stream );

其中,stream 是要刷新的流的指针。如果 stream 为 NULL,则刷新所有流的缓冲区。

8.可持久化数据的具体项目例子

这里就给大家看一看我之前写的通讯录:

c语言小课设--通讯录(动态内存管理+可持久化数据)-CSDN博客该项目实现一个通讯录功能,除了能根据具体需求扩大空间之外,也实现了最基本基本的增删查改等功能,并在退出通讯录时销毁创造的空间,从而不造成内存泄露。另外,这个项目由三部分组成,函数功能的实现在Contact.c源文件中,各种头文件、函数等声明则由文件Contact.h来实现,最后测试在源文件test.c文件中进行。https://blog.csdn.net/qq_62987647/article/details/133466779?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22133466779%22%2C%22source%22%3A%22qq_62987647%22%7D

9.总结

文件操作的学习可以让我们持久化数据,同时也理解了文件数据是怎么输入输出的。

有啥用呢?

用处大多了,比如说我们在用c语言写一个程序的时候,我们就可以将程序中的数据存放在文件中,以便于我们下次打开程序还能读取到,这也就更加符合我们的实际需求。

其实呢,当你学到文件这一块内容时,你对c语言的认知基本上有了一个新的高度,但是并不意味着是c语言学习的终点,学海无涯,我们当以谦卑的心态学习。

事已至此,好好学习,给我点赞,加油!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1079981.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

mysql面试题40:列值为null或者空字符串时,查询是否会用到索引?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:列值为null或者空字符串时,查询是否会用到索引? 当列的值为NULL时,查询可能会使用索引,但具体是否使用索引取决于数据库的优化器和查询条件。…

点向行列连边的网络流图优化成行列连边的二分图:CF1592F2

https://www.luogu.com.cn/problem/CF1592F2 做完F1&#xff0c;然后用1的结论来思考。 场上推了几个性质。首先op4的操作行列必然两两不同&#xff0c;所以op4最多 max ⁡ ( n , m ) \max(n,m) max(n,m) 次。然后手玩发现只有除 ( n , m ) (n,m) (n,m) 的三个格子都为1&am…

ChatGPT 是如何产生心智的? | 京东云技术团队

一、前言 - ChatGPT真的产生心智了吗&#xff1f; 来自斯坦福大学的最新研究结论&#xff0c;一经发出就造成了学术圈的轰动&#xff0c;“原本认为是人类独有的心智理论&#xff08;Theory of Mind&#xff0c;ToM&#xff09;&#xff0c;已经出现在ChatGPT背后的AI模型上”…

芯科蓝牙BG27开发笔记10-BG27样板调试

样板使用了1.5V电源&#xff0c;boost升压到1.8V供MCU使用&#xff0c;因此IO通信的电平需要注意&#xff1a; 不能使用常用的5V、3.3V的jlink进行调试&#xff0c;类似的uart通信也一样。 BRD4001A底板的jlink如何使用&#xff1f; 参考开发板套件的说明文档《ug551-brd4194…

purr map walk 学习教程 完整版教程学习

Function reference • purrrhttps://purrr.tidyverse.org/reference/index.htmlMap over multiple input simultaneously (in "parallel") — pmap • purrr 11 Other purrr functions | Functional Programming (stanford.edu) 关注微信&#xff1a;生信小博士 1…

【解决问题思路分析】记录hutool默认使用服务端上次返回cookie的问题解决思路

背景&#xff1a; 本服务需要调用第三方接口获取数据&#xff0c;首先调用public-key接口获取公钥&#xff0c;然后用公钥加密密码&#xff0c;将用户名和密码传入/ticket接口&#xff0c;获取Cookie和response body中的token。 排查思路 由于是调用第三方接口出现问题&…

OnlyOffice文档服务器安装及集成使用

OnlyOffice文档服务器安装及集成使用 一、安装1.使用docker安装2.开启防火墙3.配置4.访问测试 二、应用集成1.前端集成(React)(1).安装onlyoffice/document-editor-react(2).使用 ONLYOFFICE 文档 React 组件 2.后台集成(Java)(1) getFile接口(2) callback接口(3) getFile接口和…

SpringBoot拦截器实现

1.定义一个拦截器类&#xff0c;实现HandlerInterceptor接口 创建一个Interceptor类实现HandlerInterceptor接口&#xff0c;重写preHandle()&#xff0c;postHandle()&#xff0c;afterCompletion() 三个方法 如下代码&#xff0c;我们就创建了一个Spring的拦截器 /*** auth…

战神引擎传奇假设教程

战神引擎传奇假设教程 --------------------------------------------------------------------------------------------------- 传奇这款游戏可以说是一代人的回忆&#xff0c;特别是8090后&#xff0c;传奇对他们有着许许多多的难忘的回忆&#xff0c; 随着时代的发展&…

typora常用偏好设置

启用自动保存 关闭拼写检查 插入图片的设置 将图片保存在当前文件夹内 换行设置 关闭换行符的显示功能

LeetCode【155】最小栈

题目&#xff1a; 代码&#xff1a; class MinStack {Deque<Integer> xStack;Deque<Integer> minStack;public MinStack() {xStack new LinkedList<Integer>();minStack new LinkedList<Integer>();minStack.push(Integer.MAX_VALUE);}public voi…

Sub-1G射频收发器soc芯片 UM2080F32 低功耗 32 位 IoTP

UM2080F32是基于 ARM Cortex M0 内核的超低功耗、高性能的、单片集成 (G)FSK/OOK 无线收发机的 32 位SOC 芯片。 UM2080F32 工作于200MHz~960MHz 范围内&#xff0c;支持灵活可设的数据包格式&#xff0c;支持自动应答和自动重发功能&#xff0c;支持跳频操作&#xff0c;支持 …

48 路径总和 III

路经总和 III 题目规定了寻路方向&#xff08;不能折返&#xff0c;是单源向下探路&#xff0c;符合DFS&#xff09;模板1 题解1 DFS更好理解题意的版本 题解2 前缀和&#xff08;重点记忆&#xff09;前缀和 由根结点到当前结点的路径上所有节点的和(不含当前结点) 给定一个二…

面试官:谈谈 Go 内存逃逸机制

大家好&#xff0c;我是木川 一、概念 在一段程序中&#xff0c;每一个函数都会有自己的内存区域存放自己的局部变量、返回地址等&#xff0c;这些内存会由编译器在栈中进行分配&#xff0c;每一个函数都会分配一个栈桢&#xff0c;在函数运行结束后进行销毁&#xff0c;但是有…

【开发日记】Docker搭建Maven私服

文章目录 前言1、拉取镜像2、创建本地目录3、启动容器4、访问5、上传依赖6、项目配置私服 前言 Maven私服是一种特殊的远程仓库&#xff0c;它是架设在局域网内的仓库服务&#xff0c;用来代理位于外部的远程仓库&#xff08;中央仓库、其他远程公共仓库&#xff09;。 在公司…

操作系统实验一:计算机资源信 息分析(Windows 2学时)

一、实验目的 通过实验使学生进一步了解操作系统使用的计算机软硬件环境,掌握进程、线程、内存、文件等基本概念,获得某计算机中的软硬件资源信息。基本能达到下列具体的目标: 掌握获取的计算机硬件信息的方法。掌握获取计算机安装的操作系统信息的方法,分析安装的操作系统…

mybatis逆向工程

目录 第一章、mybatis逆向工程1.1&#xff09;Generator.xml配置文件1.2&#xff09;pom文件中添加mybatis代码自动生成插件1.3&#xff09;双击红色选中命令&#xff0c;生成相关文件</font>1.4&#xff09;成功生成mapper和model&#xff0c;加上注解 友情提醒 先看文…

自动化模式下,企业全面预算管理的提升

近年来&#xff0c;经济世界不确定事件的频频发生&#xff0c;让企业开始关注自身的关键财务弱点。企业在财务能力敏捷性提升的方面仍存在一定的差距&#xff0c;而在数字化转型过程中进行的投资不够&#xff0c;将难以推动企业冲出重围&#xff0c;提高前瞻性和自身预测能力。…

【已解决】socket.gaierror: [Errno -3] Temporary failure in name resolution

问题描述 今天在环境迁移的过程中遇到多个问题&#xff0c;包括ModuleNotFoundError: No module named flask&#xff0c;socket.gaierror: [Errno -3] Temporary failure in name resolution以及Downloading: "https://huggingface.co/gyrojeff/YuzuMarker.FontDetection…

2023前端面试题汇总。

一、CSS 1.说一下CSS的盒模型。 在HTML页面中的所有元素都可以看成是一个盒子盒子的组成&#xff1a;内容content、内边距padding、边框border、外边距margin盒模型的类型&#xff1a; 标准盒模型 margin border padding content 2. IE盒模型 margin content(border p…