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

前言

为什么要使用文件呢?

你是否发现,我们写的程序,退出后再次运行,是记不住上次程序的数据的。这是因为我们写的程序是存储在电脑的内存中的,如果程序退出,内存回收,所以数据就丢失了。

如果想要将数据进行持久化的保存,我们可以使用文件。

想想我们电脑上的这些文件,PPT之类的,我们写好后再次打开依然是在的。包括我们写好的代码,也在电脑的文件夹里放着。这是因为这些文件是放在电脑的硬盘上的。

硬盘上的数据除非自己修改,否则是不会丢的。下次打开程序还是在硬盘上拿到我们的数据。

 所以当我们把数据放在文件中,文件又放在硬盘上,就可以将数据进行持久化的保存。

 当然这个“持久”,也不是永久,电脑或者硬盘是可能坏的。

什么是文件?

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

我们电脑的C、D盘里进去的这些都是文件。

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

程序文件

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

数据文件

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

比如在下图中,我们的test.exe可以从data.text中读取数据(读),或输出数据到data.txt(写)中,我们的test.exe是程序文件,data.txt是数据文件。

本文主要探讨的是数据文件,也就是怎么通过C语言写代码来操作数据文件。

以前,我们处理数据的输入输出都是以终端(也就是我们运行起来看见的黑框)为对象的,即从终端的键盘输入数据,运行结果显示在显示器上。

我们用printf打印数据到终端或者说屏幕,其实就是的动作。

而从终端或屏幕输入或者读取数据,就是的动作。

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

经过本文的学习,以后我们写代码就可以将数据写到数据文件里。

文件名

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

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

如:c:\code\test.txt中,c:\code\是路径,test是文件名主干,.txt是文件后缀。

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

前面是从功能的角度来讲,分为程序文件和数据文件,其实从内容的角度来讲,文件又可以分为二进制文件和文本文件。

二进制文件和文本文件

根据数据的组织形式,数据文件被称为文本文件或二进制文件。

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

我们写上一个代码,它在内存中是以二进制存储的。

比如:

int main()
{
    int a = 2077;
    //2077的补码的二进制序列
    return 0;
}

我们2077的存储方式是在内存中存它的补码的二进制序列。

浮点数在内存中存储也是以二进制。

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

一个数据在文件中是怎么存储的呢?

字符一律以ASCII形式存储,数值型数据可以用ASCII形式存储,也可以用二进制形式存储。

字符没有办法,就只能以ASCII码形式存储。

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

注意:并不总是以二进制形式存储占用空间更少的,比如存1的时候,ASCII形式只占用1个字节,而二进制形式存储还是占用4字节。总之,要看具体情况。

具体点分析,10000的二进制序列:

0000 0000 0000 0000 0010 0111 0001 0000

按字节来排着看就是:

00000000 00000000 00100111 00010000

而将10000转换为ASCII形式存储,就是将其转换为1 0 0 0 0的分别存储,各自存储到一个字节(8bit)中去:

00110001 00110000 00110000 00110000 00110000
1        0        0        0        0

(下面是要存的字符,上面是这个字符的ASCII码值的二进制序列。1的ASCII码值是49,49的二进制序列是00110001;0的ASCII码值是48,二进制序列是00110000)

演示:

现在我们创建一个文件,重命名为test.txt

 

以这样的形式存放的时候,存的就是5个字符

这就是文本文件 ,也就是说我们用肉眼能够看得懂的。

现在,我们还可以用代码的方式造出一个二进制文件:

#include<stdio.h>
int main()
{
	int a = 10000;
	
	FILE* pf = fopen("test.txt", "wb");//打开文件test.txt,
	//w代表write,写。b代表binary,二进制。也就是二进制写的方式来打开文件
	
	fwrite(&a, 4, 1, pf);//二进制的形式写到文件中,
		//写a(提供的是地址),写4个字节,写1次,写到pf关联的文件中
	
	fclose(pf);//关闭文件
	pf = NULL;
	return 0;
}

然后运行这个程序。

然后找到我们的test.txt,点击编辑:

 所以可以看到,以文本文件的形式来解读我们写进去的二进制时,没法解读。

 怎么证明它是个二进制呢?也可以证明一下:

右击源文件,添加现有项,将我们的test.txt加入。

 

然后再打开方式里选择二进制编辑器。

 

 左侧的00000000不用看,右侧的才是文件内容。

刚才我们已经看到10000的二进制序列:

00000000 00000000 00100111 00010000

十六进制是:

0000 0000 0000 0000 0010 0111 0001 0000
0    0    0    0    2    7    1    0
00 00 27 10

因为是正整数,所以原码、反码、补码相同。但是是小端字节序存储,所以在内存中存的应该是:

10 27 00 00

 所以就是将内存里的数据不加转换直接放到文件里去了。这种文件就是二进制文件。二进制通过文本编译器打开是读不懂的。(vscode打开也不行)

在介绍文件的打开和关闭之前,还有需要铺垫的基础知识:

流和标准流

程序的数据需要输出到各种外部设备,比如我们可以把数据放到文件、光盘、软盘(早期)、U盘、网络、屏幕上等。 

也需要从外部设备获取(读取)数据。

不同的外部设备的输入输出操作各不相同,为了方便程序员对各种设备进行方便的操作,我们抽象出了的概念。

写程序只要关注的操作就可以了,程序先写到流里,流再对接各种各样的外部设备。

可以把流想象成流淌着字符的河。

至于流怎么把数据给这些外部设备,就是底层的事情了。

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

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

一般的三个步骤:

1.打开流

2.读/写

3.关闭流

 

标准流

针对不同的外部设备,流分为很多种,有文件流、标准流等。

可以回想一下我们平时写代码:

scanf,从键盘上取数据;printf,将数据到屏幕上。我们的键盘和屏幕也是外部设备,但是好像并没有打开流这样的具体操作。为什么呢?

这是因为C语言程序在启动(运行)的时候,默认打开了3个流:

stdin——标准输入流,在大多数的环境中是从键盘输入,scanf函数就是从标准输入流(键盘)中读取数据。

stdout——标准输出流,在大多数环境就是我们的显示器界面(屏幕),printf函数就是将信息输出到标准输出流中。

stderr——标准错误流,大多数环境中输出到显示器界面。

 这三个流是默认打开的,所以我们在使用scanf、printf等函数时直接就对标准流进行了操作,并没有去关心这个流怎么打开。

stdin stdout stderr三个流的类型是:FILE*,通常称为文件指针。 

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

通过文件指针,我们就能找到流,然后再通过流的操作,操作外部设备。

到此,文件操作(上卷)就结束了,之后还会有文件操作(下卷),祝阅读愉快^_^

  • 24
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值