C语言中的文件操作
大家好,今天为大家带来C语言中关于文件操作的相关知识
1.我们为什么要使用文件?
我们写的程序是储存在电脑的内存中的,如果程序退出,内存回收,数据就丢失了,等再次运行程序,是看不到上次程序的数据的,如果要将数据进行持久化的保存,我们可以把数据放在硬盘上的文件上进行保存。
2.程序文件的分类
在程序文件中,我们一般谈的有两种:程序文件、数据文件(按功能的角度分类)
2.1程序文件
程序文件包括源程序文件(后缀为.c),目标文件(windows环境下后缀为.obj)和可执行文件(windows环境下后缀为.exe)
2.2数据文件
文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件
3.二进制文件和文本文件
我们知道,数据是以二进制的形式存在内存中的,如果我们不加转化的把二进制形式的数据存在外存的文件中,这种文件就是二进制文件;而先将数据转换为ASCII值的形式,再存在外存的文件中,这就是文本文件。
举个例子:
我们要将10000存在磁盘中,那么下图中的上方就是以ASCII码值的形式存放的(1的ASCIII值为49,0的ASCII码值为48)。下面就是以二进制的形式存放的。

4.文件的打开和关闭
4.1流和标准流
4.1.1流
我们程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输入输出操作各不相同,为了方便程序员对各种设备进行方便的操作,我们抽象出了流的概念,我们可以把流想象成流淌着字符的河——当我们要向"流"里写数据或从"流"里读取数据时就要打开"流"
4.1.2标准流
在程序启动的时候会默认打开3种流:
- stdin - 标准输入流,在大多数的环境中从键盘输入,scanf 函数就是从标准输入流中读取数据。
- stdout - 标准输出流,大多数的环境中输出至显示器界面,printf 函数就是将信息输出到标准输出流中。
- stderr - 标准错误流,大多数环境中输出到显示器界面
这样就知道了我们在程序运行的时候并没有打开流,但我们还能在键盘上读取数据及在显示屏上输出数据的原因了
4.2.文件指针
在C语言中我们是以文件指针(类型是FILE*)来进行流的各种操作的
在C语言的头文件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 结构的变量,并填充其中的信息,使用者不必关心细节
4.3文件的打开和关闭
ANSIC规定使用fopen函数来打开文件fclose函数来关闭文件
4.3.1 fopen函数

其中参数filename是待打开的文件名,mode是打开文件的方式,函数的返回类型是FILE*的指针
文件打开方式如下:

举个例子:
我们要在不存在的 " test.txt " 上分别要写和读文件

读文件:

写文件:


可以看到当我们读不存在的文件时,编译器直接报错,意思是你指定的文件或文件夹路径不存在。
而当我们去写一个不存在的文件时,会自动新建一个文件。
其他操作同理.....
4.3.2 fclose函数

其中stream是文件指针所指向流的对象,返回值为整型,若成功关闭返回0,若失败返回EOF
当然,我们可以在文件关闭后将文件指针设为空指针,以此来避免成为野指针
5.文件的顺序读写
5.1文件顺序读写函数介绍:

上面的所有输入流包括标准输入流在内的所有输入流(如:文件输入流),所有输出流同理。
下面对上述函数举例:(我现在在 " test . txt " 文件里写了以下字符串)

5.1.1 fgetc函数(向指定文件读取字符)

fgetc函数读取字符从FILE*指针指向文件(流)的第一个字符开始(其他函数的该参数作用大致相同,对此下文不再解释)
每读一个字符文件中的光标向后移一位(下文同理),
返回类型为int类型,是因为所有字符可用唯一的ASCII码值表示,且当读到文件末尾时返回EOF(-1)


5.1.2 fgets函数(向指定文件读取字符串)

其中str是用来接收文件内容的字符数组(因为数组的数组名就是首元素地址,所以直接传数组名就行),num是拷贝的字符个数
注:
a. fgets实际上一次性只拷贝num - 1个字符,因为最后要放入' ' ,所以num应该是>=2的
b. 当字符储存到 ' '(换行符)时下一个字符会直接存入 ' '
c. 当遇到文件末尾时会返回空指针(NULL)


5.1.3 fscanf函数(向指定文件读取字符,不包含空格)

与scanf函数参数大致相同,只有一个FILE指针所指向的输入流不同

当然我们若在文件指针FILE*处写上stdin(标准输入流),就能实现scanf函数的功能(在键盘上读取数据)

5.1.4 fputc函数(向指定文件输入字符)
现在我们将" test . txt "文件内的内容清空便于观察

参数character,就是输入的字符(上文有说为什么是整型)
程序执行成功就不会报错,我们打开文件查看就行了,当然也可以用fgetc(fgets、fscanf)函数检验(要先换成"读"的形式打开文件)

5.1.5 fputs函数(向指定文件输入字符串)

和上面同理:将字符串str的内容拷贝到指定的文件(流)里

5.1.6 fprintf函数(向指定文件输入字符串)

同样的,这个函数和printf函数只有一个参数不同

当然我们若在文件指针FILE*处写上stdout(标准输出流),同样可以代替printf函数

5.1.7 fwrite函数(向文件中以二进制的形式存入数据)

打开文件方式用" wb ",
返回类型是放入字符的个数
ptr为指向要写入的元素数组的指针,转换为cont void*
size为写入元素的字节大小
count为每一次要写入元素的数目

可以看见虽然我们把数据放进去了,但是我们读不懂,这是给计算机识别的,当然我们可以用其他方式来读取(以VS为例)

我们按照以上操作步骤,就能看见数据在内存中储存的形式(VS是小端存储)

这也就代表数据确实存入文件中里了
5.1.8 fread函数(向文件中以二进制的形式读取数据)

参数方面与fwrite函数相同
我们就以上文已经输入的数据为例

可以看到fread函数把文件中的数据成功读取并拷贝到数组中
6.文件的随机读写
当我们想要在文件中的某一个位置补充内容(或读取某一个指定的内容),我们若使用上面的函数会比较麻烦,那么有没有其他的方法呢?
这时候要用到 fseek 、 ftell 和 rewind 这三个函数了,这三个函数可以移动光标位置
6.1 fseek函数

其中offset参数是距离origin的偏移量(关于偏移量在我上一篇博客有具体描述,在此不过多赘述)
origin具体内容如下:

大致意思为:
SEEK_SET为文件起始位置
SEEK_CUR为光标当前所在位置
SEEK_END为文件末尾
举个例子:现在我将以下内容放在文件中,现在要读取 ' w ' 这个字符

我们有以下三种方法:
a. 目标字符与文件开头的偏移量为6,orogin取SEEK_SET

b. 我们先假设把光标指向 " hello " 的 ' o ' 处(' w ' 与 ' o ' 的偏移量为2),orogin取SEEK_CUR

c. 目标字符与结尾的偏移量为5,orogin取SEEK_END

6.2 ftell函数(计算偏移量)

返回类型是文件指针当前指向内容距文件开头的偏移量
通过这个函数再搭配fseek函数就能够计算文件的字符个数

6.3 rewind函数

该函数可以让光标刷新会文件开头

7.文件读取结束的判定
当我们在读取文件的时候,如果突然停了下来,我们不知道是因为文件读取结束而停下来,还是因为读取失败而停下来
这时就有两个函数——feof 、ferror函数
当我们打开一个流的时候,流上会有两个标记值:
1. 是否与到文件末尾(feof检验,被标记返回非零值)
2. 是否遇到错误(ferror检验,被标记返回非零值)

以上就是全部内容,感谢阅读,希望能够帮到你,也欢迎大家指错及补充







