文章目录
- 一、文件的概念和分类
- 1. 概念和分类
- 2. 文件名
- 3. 数据文件
- 三、文件操作
- 1. 文件的打开和关闭
- 1.1 流
- 1.2 文件指针
- 1.3 文件的打开和关闭
- 2. 文件的顺序读写
- 3. 文件的随机读写
- 4. 文件读取的判定
- 5. 文件缓冲区
一、文件的概念和分类
1. 概念和分类
文件是用来保存数据的。如果没有文件,我们写的程序会直接存储在电脑内存中,如果程序退出,内存被释放回收,数据就丢失了。所以如果要将数据进行持久化的保存,我们就可以使用文件来保存数据。
在生活中,我们一般直接将电脑磁盘中的文件称之为文件。而在程序设计中,我们所说的文件一般分为两种:程序文件、数据文件。
- 程序文件包括:源程序文件(后缀为.c)、目标文件(Windows环境下后缀为.obj)、可执行程序(后缀为.exe)以后会再次介绍的。
- 数据文件的内容,是程序运行时读写的数据,包括程序运行时需要从中读取数据的文件,或者输出内容的文件。
在之前C语言的学习过程中,我们对数据的输出输入都是以终端为对象的,即从终端的键盘输入数据,运行结果显示到显示器上。但有时候我们也需要把数据输出到磁盘中,也需要从磁盘中读取数据到内存中来使用,这时就是对磁盘里的文件进行文件操作了。
2. 文件名
我们还要知道的是:一个文件通常要有一个唯一的文件标识以便于使用者识别和引用,这就是文件名,通常包含“文件路径、文件名主干、文件后缀”三部分。
举个例子,在我的电脑磁盘中有:
右键点击复制文件地址,能得到这个文件的文件名是:
D:\ACG电吉\春日影\Final春日影 吉他.pdf
其中,这个文件本身的名字“Final春日影 吉他”,是文件名主干。
前面的“D:\ACG电吉\春日影\”,是文件路径。
最后的“.pdf”,是文件后缀。
很简单对吧。
3. 数据文件
数据文件还可以再分为文本文件和二进制文件。
- 文本文件:数据以ASCII值的形式存储的文件
- 二进制文件:数据以二进制的形式存储的文件
字符型数据一律用ASCII值形式存储,数值型数据既可以用ASCII值形式存储也可以用二进制形式存储。
它们的区别是什么呢?举个栗子,整数12345
- 如果以ASCII形值式输出到磁盘中,则占用五个字节(一个字符占据一个字节),内容是00110001 00110010 00110011 00110100 00110101(即ASCII值49、50、51、52、53,对应字符“1”、“2”、“3”、“4”、“5”)
- 如果以二进制形式输出到磁盘中,则占用四个字节(int大小),内容是00000000 00000000 00110000 00111001(就是12345的二进制表示)
三、文件操作
1. 文件的打开和关闭
1.1 流
流是计算机领域一个抽象但重要的概念。
简单来说,程序需要跟各种外部设备交互,外部设备包括键盘、显示器、磁盘、U盘、网络等等,程序的数据需要输出到各种外部设备上,也需要从各种外部设备获取数据,不同的外部设备的输入输出操作各不相同。为了方便程序员对各种设备的操作,人们抽象出了“流”的概念。“流”就像一条流淌着数据的河,程序和外部设备将数据都投入这条“河”中,或者各取所需的数据。C语言内部处理好外部设备和流的数据传递,而程序设计者只要再考虑程序如何与流交互就好了。
一般情况下,我们想要往流里写数据,或者从流里读取数据,都是先打开流,然后再操作。
那么,为什么我们以前写C语言程序,从键盘上读取数据,向屏幕上输出数据,并没有打开流的操作呢?这是因为C语言程序在启动时,默认会打开三个流:
- stdin(标准输入流):它在大多数环境下能从键盘输入数据,scanf函数就是从这个流中读取数据的
- stdout(标准输出流):它在大多数环境下能输出数据至显示器界面,printf函数就是将信息输出到这个流中的
- stderr(标准错误流):它在大多数环境下能输出数据至显示器界面
这三个流的类型是FILE*
,被称为文件指针。在C语言中,就是通过文件指针来维护流的各种操作的。
1.2 文件指针
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息,比如文件的名字、状态、位置等等。这些信息被保存在一个结构体变量中,该结构体变量是由系统声明的,被typedef重命名为FILE。不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异。
使用者打开一个文件时,系统会根据文件的情况自动创建一个FILE类型的变量,自动填充好内部信息,使用者不必关心具体细节。
为了维护这个FILE结构的变量,一般使用FILE*类型的指针,这就是文件指针。比如,定义一个FILE* pf;
可以使pf指向某个文件的文件信息区,通过该文件信息区的信息来操作该文件,换句话说,通过文还指针能够间接找到与它相关联的文件。
1.3 文件的打开和关闭
读写文件之前应该先打开文件,使用后应该关闭文件。ANSI C规定使用fopen函数打开文件,fclose函数关闭文件。它们(和以下函数)包括在头文件stdio.h中
fclose的使用方式很简单,参数就是需要关闭的文件的文件指针。
fopen的使用较为复杂:
FILE* fopen(const char* filename, const char* mode);
它的返回值是打开的文件的文件指针。
它的第一个参数是文件名,第二个参数是文件的打开模式,有以下模式及其作用:
文件打开模式 | 含义 | 如果指定文件不存在 |
---|---|---|
r(只读) | 为了输入数据,打开一个文本文件 | 返回空指针 |
w(只写) | 为了输出数据,打开一个文本文件 | 创建一个新的文件(如果文件存在,会清空原内容) |
a(追加) | 向文本文件尾添加数据 | 创建一个新的文件 |
rb(只读) | 为了输入数据,打开一个二进制文件 | 返回空指针 |
wb(只写) | 为了输出数据,打开一个二进制文件 | 创建一个新的文件 |
ab(追加) | 向二进制文件尾添加数据 | 创建一个新的文件 |
r+(读写) | 为了读写数据,打开一个文本文件 | 返回空指针 |
w+(读写) | 为了读写数据,打开一个文本文件 | 创建一个新的文件 |
a+(读写) | 在文本文件尾读写数据 | 创建一个新的文件 |
rb+(读写) | 为了读写数据,打开一个二进制文件 | 返回空指针 |
wb+(读写) | 为了读写数据,创建一个二进制文件 | 创建一个新的文件 |
ab+(读写) | 在二进制文件尾读写数据 | 创建一个新的文件 |
举例,一开始我的项目文件夹里没有data.txt这个文件
如果有FILE* pf = fopen("data.txt","r");
:
如果是FILE* pf = fopen("data.txt","w");
就会在这个代码项目的文件夹中创建这样一个文件:
当然,我们还可能需要从电脑的其他磁盘上的其他文件中读写数据,就需要明白绝对路径和相对路径的表示方法:
- 绝对路径就是一个文件的完整文件名,比如之前提到过的 D:\ACG电吉\春日影\Final春日影 吉他.pdf
- 相对路径要用
.
或..
表示,.
表示当前路径,..
表示上一级路径。每一级中间本来需要用\
分割,但在C语言中需要被转义,即要用转义字符\\
表示\
举个栗子,我的电脑中一开始没有data.txt这个文件,我的C语言程序源文件位置如下:
如果在程序中以w模式打开一个D盘文件data.txt,就会自动创建
- 用绝对路径表示:
fopen("D:\\data.txt","w");
- 用相对路径表示:
fopen(".\\..\\..\\..\\..\\data.txt","w");
都是成功的
2. 文件的顺序读写
文件的顺序读写涉及到一系列函数:
函数 | 功能 | 适用于 |
---|---|---|
fgetc | 字符输入 | 所有输入流 |
fputc | 字符输出 | 所有输出流 |
fgets | 按文本行输入 | 所有输入流 |
fputs | 按文本行输出 | 所有输出流 |
fscanf | 格式化输入 | 所有输入流 |
fprintf | 格式化输出 | 所有输出流 |
fread | 二进制输入 | 文件输入流 |
fwrite | 二进制输出 | 文件输出流 |
这些函数的使用大同小异,我们就举两个例子,其余大家自己查阅了解即可
fputs函数的第一个参数是要写入的字符串,第二个参数是要写进的文件
fgetc函数的参数是要从中读取字符的文件
#include<stdio.h>
int main()
{
FILE* pf = NULL;
pf = fopen("data.txt", "w");
fputs("TECH OTAKUS SAVE THE WORLD", pf);
fclose(pf);
pf = fopen("data.txt", "r");
int n = 0;
while(n++ < 5)
printf("%c\n", fgetc(pf));
fclose(pf);
return 0;
}
结果很显然,程序创建了一个文本文件写入了一些内容,然后打印出前四个字符。
3. 文件的随机读写
上面的函数只能对文件内容按顺序读写,有时我们也需要对内容按照特定顺序读写,这就是文件的随机读写。要用到以下函数:
-
fseek
这个函数能改变文件光标的位置,“文件光标”就是读写数据的位置,可以理解成我们在电脑手机上打字的这个光标。
第一个参数是文件指针,第二个参数是你想要的偏移量,第三个参数是你想从哪个位置计算偏移量。第三个参数是有三个取值的:SEEK_SET是文件的开头,SEEK_CUR是当前光标的位置,SEEK_END是文件的末尾。
光标位置是怎么决定的呢?比如文件指针pf刚刚进行了写数据,其指向的文件里刚写下了abcdef
,此时光标在末尾,即f
后的位置。如果有fseek(pf, 2, SEEK_SET);
光标在文本开头后偏移2个位置,即b和c中间。假如我们再写一次数据,就会从这里开始写,新写入的数据会覆盖掉刚才的几个字符:偏移量负值代表左偏移,正值代表右偏移。所以,对于abcdef
,fseek(pf, 2, SEEK_SET)
和fseek(pf, -4, SEEK_END)
的效果其实是一样的再举例一个用法,fgetc读取字符是从文件开头向后一个一个读的。因为光标起始位置是开头,每次读光标之后的一个字符,然后光标向右偏移1。但我们不想按这样的顺序读数据时,就可以用fseek函数实时调整光标的位置了。
-
ftell很简单,它的参数是文件指针,这个函数能返回这个文件中当前光标相对于开头的偏移量
-
rewind这个函数能让参数文件的光标返回起始位置
4. 文件读取的判定
不同的函数读取到文件末尾时的标识不一样,比如fgets读取结束会返回NULL,fgetc读取结束会返回EOF,等等。对于二进制文件,fread的返回值小于实际要读取的个数,说明读取结束
5. 文件缓冲区
最后一个简单的知识点:ASNI C标准采用“缓冲文件系统”处理数据文件,它指系统自动在内存中为程序每一个正在使用的文件开辟一块“文件缓冲区”。从内存向磁盘输出数据,会先送到文件缓冲区,装满缓冲区后才一起送到磁盘上。如果从磁盘向计算机输入数据,则从磁盘文件中读取数据输入到内存缓冲区,然后再从缓冲区逐个地将数据送到程序数据区。缓冲区的大小由C编译系统决定。因为由缓冲区的存在,所以C语言在进行文件操作时,需要刷新缓冲区或者结束时关闭文件。如果不做,可能有读写文件的问题。
本篇完,感谢阅读~