【C语言】—— 文件操作(上)

【C语言】------ 文件操作(上)

一、 为什么使用文件

  我们为什么要使用文件呢?回答这个问题之前,我们先来看一段代码:

c 复制代码
#include<stdio.h>

int main()
{
	int n = 0;
	scanf("%d", &n);
	printf("%d\n", n);

	return 0;
}

  这段代码创建了一个变量 n n n,当程序运行时,我们输入一个值(比如说 10 )存在变量 n n n 中,当我们下一次运行程序时,这个10 不见了,它的值又恢复到 0,我们又得重新输入一个值。

  原来,像 n n n 这样的变量是在内存里创建的,当程序结束时,刚刚申请的 4 个字节又还给了操作系统 ,当程序再次运行时, n n n中的值依然是 0

  因此,如果将数据存在内存中,当程序结束,下次再运行时,这个值就不见了,它无法持久化地保存起来

  怎么办呢?

  这时我们想,如果我们在电脑上创建了一个文件,文件中存这一个 10。这个文件是放在硬盘上的(比如C盘、E盘)。这即使把电脑关机,下次打开里面依然存的是 10。

  这是因为,当我们把数据放在文件中,即硬盘上时,这些数据会持久化地保存下来。而相对的,放在内存中数据只要程序退出,数据就丢失了。

  因此当我们想把数据持久化地保存下来时,就需要文件

  

二、 什么是文件

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

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

  

2.1、 程序文件

  所谓的程序文件就是我们写出来的代码,程序文件包括源程序文件 (后缀文 . c .c .c),目标文件 ( w i n d o w s windows windows 环境后缀为 . o b j .obj .obj),可执行程序 ( w i n d o w s windows windows环境后缀为 . e x e .exe .exe)

  

2.2、 数据文件

  什么是数据文件呢?当我们写程序,从某一个文件中读取数据;或者把一些数据写到文件中,这种文件就称为数据文件。

  本章我们讨论的内容主要是数据文件。

  在之前的学习中,我们所处理的输入输出都是以终端为对象的,即:从终端的键盘输入数据,运行结果显示到显示器(屏幕)上。

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

  

2.3、 文件名

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

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

  如:c:\code\test.txt

  • c:\code\ 为文件路径
  • t e s t test test 为文件名主干
  • t x t txt txt 为文件后缀

  

2.4、二进制文件与文本文件

  根据文件的组织形式,数据文件一般被称为文本文件和二进制文件

  数据在内存中以二进制的形式存储,如果不加转换的输出到外出文件中,就是二进制文件。

  前面曾提到的 . o b j .obj .obj文件就是一种 二进制文件,通过文本编辑器打开是一堆乱码,我们是看不懂的

  

  如果要求在外存上以 A S C I I ASCII ASCII 码的形式存储,则需要再存储前转换。以 A S C I I ASCII ASCII 字符存储的文件就是文本文件

  

  那么一个数据在文件中是如何存储的呢?

  字符一律以 A S C I I ASCII ASCII 码形式存储,数值型数据及可以用 A S C I I ASCII ASCII 码形式存储,也可以使用二进制形式存储。

  什么意思呢?假设现在有一个整数 10000:

  如果以 A S C I I ASCII ASCII 码的形式存储,则磁盘中占用 5 个字节(一个字符一个字节):一个字符 '1',四个字符 '0'

  如果以二进制的形式存储,磁盘上只占4个字节(一个整型)

  

  而以 A S C I I ASCII ASCII 码形式存储则为文本文件;以二进制形式存储则为二进制文件

  
测试代码:

c 复制代码
#include<stdio.h>

int main()
{
	int a = 10000;
	FILE* pf = fopen("test.txt", "wb");
	fwrite(&a, 4, 1, pf);//二进制的形式写到文件中
	fclose(pf);
	pf = NULL;
	return 0;
}

  这段代码可能有些看不懂,但是没关系,我们只需知道其大致意思即可:这段代码就是将 10000 以二进制的形式写到文件中

  执行程序后,我们直接打开文件:

  

  我们再通过二进制的形式打开文件


VS上打开二进制文件的方法


10000 在二进制文件中

  那 10 和 27 又是什么意思呢?其实就是 0001 0000 和 0010 0111 的十六进制表示,同时,因为是小端字节序存储,因此存储时反过来的。

  

三、 文件的打开和关闭

3.1、流和标准流

(1)流

  我们程序的数据要输出到各种外部设备(屏幕、硬盘、U盘、网络上),也需要从外部设备(键盘、文件、网络上、U盘)获取数据,不同的外部设备的输入输出的操作各不相同 ,为了方便程序员对各种设备进行方便操作,我们抽象出 流 的概念,我们可以把流想象成流淌着数据的河

  没有流,程序员就要学会如何从各种不同的设备上输入输出数据,而有了流,程序员只需知道如何将数据从流中取出和写入流即可,至于流如何与外部设备之间进行交流,我们并不关心,这是C语言底层和操作系统要关系的事情。这样大大简化了程序员学习编程的难度

  C程序针对文件、画面、键盘等的数据输入输出操作都是通过操作的。

  一般情况下,我们要想向流里写数据,或者从流中读取数据,都是要打开流,然后操作

  进行文件操作,打开文件其实就是打开流,然后再对流进行读写

(2)标准流

  那为什么我们从键盘输入数据,向屏幕上输出数据,并没有打开流呢?

  那是因为C语言程序在启动的时候,默认打开了 3 个流

  • stdin(标准输入流) :在大多数的环境中从键盘输入, s c a n f scanf scanf 函数就是从标准输入流中读取数据。
  • stdout(标准输出流) :大多数的环境中输出至显示器界面, p r i n t f printf printf函数就是将信息输出到标准输出流中。
  • stderr(标准错误流) :大多数环境中输出到显示器界面

  这是默认打开了的三个流,我们使用 s c a n f scanf scanf、 p r i n t f printf printf 等函数就可以直接进行输入输出操作的。

   s t d i n stdin stdin、 s t d o u t stdout stdout、 s t d e r r stderr stderr 三个流的类型是: F I L E FILE FILE* ,通常称为文件指针。

  C语言中,就是通过 F I L E FILE FILE* 的文件指针来维护流的各种操作

  

3.2、文件指针

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

  

  每一个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存储文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名 F I L E FILE FILE。

  

  例如在VS2013编译器环境提供的 s t d i o . h stdio.h stdio.h 头文件中有以下的文件类型声明:

c 复制代码
struct _iobuf {
	char* _ptr;
	int _cnt;
	char* _base;
	int _flag;
	int _file;
	int _charbuf;
	int _bufsiz;
	char* _tmpfname;
	};

typedef struct _iobuf FILE;

  

  不同的编译器的 F I L E FILE FILE 类型包含的内容不完全相同,但是大同小异。

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

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

  下面我们可以创建一个 F I L E FILE FILE *的指针变量

  

c 复制代码
FILE* pf;

  

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

  

  

四、文件的打开和关闭

  文件在读写之前应该先打开文件,在使用结束之后应该关闭文件。

  在编写程序的时候,在打开文件的同时,都会返回一个 FILE* 指针变量指向该文件,也相当于建立了指针和文件的关系。

   A N S I ANSI ANSI  C C C规定使用 f o p e n fopen fopen函数来打开文件, f c l o s e fclose fclose函数来关闭文件。


f o p e n fopen fopen 函数(打开文件)

  • 如果文件打开成功,返回一个FILE* 指针,打开失败则返回空指针
  • f i l e n a m e filename filename 表示要打开的文件名,其应符合文件的命名规范,可以包含文件路径
  • m o d e mode mode 表示文件的打开模式,下面都是文件的打开模式
文件使用方式 含义 如果指定文件不存在
"r" (只读) 为了输入数据,打开一个已经存在的文本文件 出错
"w" (只写) 为了输入输出数据,打开一个文本文件 建立一个新的文件
"a"(追加) 向文本文件尾添加数据 建立一个新的文件
"rb"(只读) 为了输入数据,打开一个二进制文件 出错
"wb"(只写) 为了输出数据,打开一个二进制文件 建立一个新的文件
"ab"(追加) 向一个二进制文件尾添加数据 建立一个新的文件
"r+"(读写) 为了读和写,打开一个新的文件 出错
"w+"(读写) 为了读和写,创建一个新的文件 建立一个新的文件
"a+"(读写) 打开一个文件,在文件尾进行读写 建立一个新文件
"rb+"(读写) 为了读和写,打开一个二进制文件 出错
"wb+"(读写) 为了读和写,创建一个新的二进制文件 建立一个新的文件
"ab+"(读写) 打开一个未禁止文件,在文件尾进行读和写 建立一个新的文件

  注:" w w w"(只写)会将原文件的内容全部清空,再从第一位开始写入数据;"a"(追加) 是从文件末尾开始追加

  


f c l o s e fclose fclose 函数(关闭文件)

  使用 f c l o s e fclose fclose 函数将文件关闭后,要将该文件指针置空(如 f r e e free free 函数一样)

  

  

  可能有些小伙伴对输入输出的关系还有些糊涂,我们可以来看下面一张图

  其实,究竟是输入还是输出,应该站在程序的角度分析:程序从外界获取数据,是输入;程序向外输出数据,是输出。

  

下面,我们先来尝试打开和关闭一个文件

c 复制代码
#include<stdio.h>

int main()
{
	FILE* pf = NULL;
    //打开文件
	pf = fopen("test.txt", "r");
	//文件操作
	if (NULL == pf)
	{
		perror("fopen fail");
		return 1;
	}

	//读文件

	//关闭文件
	fclose(pf);
	pf = NULL;
}
  • 前面曾提到,文件打开失败时, f o p e n fopen fopen 函数会返回一个空指针,因此使用 f o p e n fopen fopen 函数打开一个文件,要对其返回值进行判断。
  • r e t u r n return return 1 :表示的是程序异常,提前结束程序,正常结束时返回0
  • 使用 f c l o s e fclose fclose 函数将文件关闭,别忘了将指针置空,如同 f r e e free free函数一样。
      
      
    未完待续······
相关推荐
Scott9999HH6 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
码智社7 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海7 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖8 小时前
JDK 26 新特性详解
java·开发语言
马优晨8 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
a11177610 小时前
2FA 验证码生成器(github登录验证 app)
笔记·学习
我的xiaodoujiao10 小时前
API 接口自动化测试详细图文教程学习系列32--Allure测试报告2
python·学习·测试工具·pytest
人邮异步社区10 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大11 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai11 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang