目录
前言
二、什么是文件
2.1文件的概念
2.2程序文件
2.3数据文件
2.4文件名
2.5二进制文件和文本文件
三、文件操作
3.1文件指针
3.2文件的打开与关闭
四、文件的顺序读写
4.1fgetc
4.2fputc
4.3fputs
4.4fgets
总结
前言
我们知道电脑上有许许多多的文件,但为什么数据要用文件来储存呢?为什么要使用文件呢?且待我细细道来。
一、为什么使用文件
我们知道,我们写的程序什么的数据什么的都存在文件中,倘若没有存在文件中,那么它们就存在了电脑的内存中,而且如果程序退出的话,内存就会进行回收,我们的数据就也回收了,也就丢失了,等到程序再次运行,是看不见上次程序的数据的,如果将数据进行持久化的保存,我们就可以使用文件。
二、什么是文件
2.1文件的概念
磁盘(硬盘)上的文件是文件。文件是计算机中的一种数据存储形式,用来存储和组织数据的集合。文件可以包含文本、图像、音频、视频等各种形式的数据。在计算机系统中,文件通常以扩展名作为标识,不同的扩展名代表不同的文件类型。文件可以存储在计算机的硬盘、固态硬盘、光盘等各种存储介质上,并可以通过操作系统提供的文件系统进行读取、写入、复制、移动等操作。
但是在程序设计中,我们一般谈的文件有两种:程序文件、数据文件(从文件功能的角度来分类的)。
2.2程序文件
程序文件包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)。例如下面:
2.3数据文件
文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。
我们以前会把所处理的数据输入输出都是以终端为对象的,即从终端的键盘输入数据,运行结果显示到显示器上。其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上吧数据读取到内存中使用,这里处理的就是磁盘上的文件。
2.4文件名
一个文件要有一个唯一的文件标识,以便用户识别和引用。文件名包含3个部分:文件路径+文件名主干+文件后缀
例如:D:\仓库code\lianxi.c
为了方便起见,文件标识常被称为文件名。
2.5二进制文件和文本文件
根据数据的组织形式,数据文件被称为文本文件或者二进制文件。数据在内存中以二进制的形式存储,如果不加转换的输出到外存的文件中,就是二进制文件。如果要求在外存上以ASCLL码的形式存储,则需要在存储前转换。以ASCLL字符的形式存储的文件就是文本文件。
字符一律以ASCLL形式存储,数值型数据既可以使用ASCLL形式存储,也可以使用二进制形式存储。如果有一个整数10000,以ASCLL码形式输出到磁盘,则磁盘中占用5个字节(每一个字符一个字节),而二进制形式输出的的话,则在磁盘上只会占4个字节。
三、文件操作
3.1文件指针
缓冲文件系统中,主要的就是“文件类型指针”,也就是“文件指针”。每一个文件在内存中都会开辟一个文件信息区,用来存放文件的相关信息(如文件的名字,文件状态以及文件当前的位置等等)。这些信息是保存在一个结构体变量中的。该结构体类型是由系统声明的,取名为FILE。
不同的编译器不同环境stdio.h头文件中的文件类型申明是不一样的:
我们可以看到VS2022中对于FILE的申明定义。下面是VS2013中对FILE的申明定义:
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;
这里就使用FILE创建了一个pf的指针,也就是文件指针变量。定义pf就是一个指向FILE类型数据的指针变量,可以使用pf,指向某一个文件的文件信息区,通过文件信息区中的信息就可以访问数据,也就是说通过文件指针变量就可以间接的找到与它关联的文件。
3.2文件的打开与关闭
文件在读取之前应该打开文件,读取完了之后应该关闭文件。文件打开的时候,会返回一个FILE*的指针变量指向该文件,也相当于建立的文件和指针的关系。
C语言中规定使用fopen函数来打开文件,fclose来关闭文件。
FILE * fopen ( const char * filename, const char * mode );
fopen里面第一个参数是文件名也就是文件的地址,地址可以用绝对地址或者是相对地址。第二个参数是以什么方式访问文件。
这里可以通过以下的方式访问:
read:打开文件进行输入操作。该文件必须存在。write:为输出操作创建一个空文件。如果同名的文件已经存在,则其内容将被丢弃,并将该文件视为新的空文件。“a”append:打开文件以便在文件末尾输出。输出操作总是在文件末尾写入数据,展开它。重定位操作(fseek, fsetpos, rewind)将被忽略。如果文件不存在,则创建该文件。“r+”read/update:打开一个文件进行更新(包括输入和输出)。该文件必须存在。“w+”write/update:创建一个空文件并打开它进行更新(输入和输出)。如果同名文件已经存在,其内容将被丢弃,并将该文件视为新的空文件。“a+”append/update:打开一个文件进行更新(包括输入和输出),所有输出操作都在文件末尾写入数据。重定位操作(fseek、fsetpos、rewind)会影响下一个输入操作,但输出操作会将位置移回文件末尾。如果文件不存在,则创建该文件。
⽂件使⽤⽅式 | 含义 | 如果指定⽂件不存在 |
“r”(只读) | 为了输⼊数据,打开⼀个已经存在的⽂本⽂件 | 出错 |
“w”(只写) | 为了输出数据,打开⼀个⽂本⽂件 | 建⽴⼀个新的⽂件 |
“a”(追加) | 向⽂本⽂件尾添加数据 | 建⽴⼀个新的⽂件 |
“rb”(只读) | 为了输⼊数据,打开⼀个⼆进制⽂件 | 出错 |
“wb”(只写) | 为了输出数据,打开⼀个⼆进制⽂件 | 建⽴⼀个新的⽂件 |
“ab”(追加) | 向⼀个⼆进制⽂件尾添加数据 | 建⽴⼀个新的⽂件 |
“r+”(读写) | 为了读和写,打开⼀个⽂本⽂件 | 出错 |
“w+”(读写) | 为了读和写,建议⼀个新的⽂件 | 建⽴⼀个新的⽂件 |
“a+”(读写) | 打开⼀个⽂件,在⽂件尾进⾏读写 | 建⽴⼀个新的⽂件 |
“rb+”(读写) | 为了读和写打开⼀个⼆进制⽂件 | 出错 |
“wb+”(读写) | 为了读和写,新建⼀个新的⼆进制⽂件 | 建⽴⼀个新的⽂件 |
“ab+”(读写) | 打开⼀个⼆进制⽂件,在⽂件尾进⾏读和写 | 建⽴⼀个新的⽂件 |
int fclose ( FILE * stream );
fclose就是把之前用来接收的指针传进去,就关闭了。
If the stream is successfully closed, a zero value is returned.
On failure, EOF is returned.
我们可以看到,如果关闭成功最后返回的就是0,如果关闭失败那么返回的就是EOF。
这样我们就可以通过代码来简单的演示一下:
我们可以首先在D盘project目录下创建一个text.tst文件,之后同过代码来访问:
#include<stdio.h>
int main()
{FILE* df = fopen("D:\\project\\text.txt","r");if (df == NULL){perror("FILE::");return 1;}else{printf("访问成功!\n");}int i=fclose(df);if (i == 0){df = NULL;return 0;}elseperror("fclose::");return 0;
}
这里就是通过read的方式访问这个text,当然是访问到了,所以最后会打印访问成功,而且也关闭成功。
我们如果把那个text删除,看看运行结果如何:
这时候就会说FILE打开文件这块没有这个文件或者目录。
基于fopen和fclose就可以实现文件的打开和关闭。如果方式变为“w”,那么会把之前里面的数据进行清除,把上一次的信息都销毁掉。
四、文件的顺序读写
函数名 | 功能 | 适⽤于 |
fgetc | 字符输⼊函数 | 所有输⼊流 |
fputc | 字符输出函数 | 所有输出流 |
fgets | ⽂本⾏输⼊函数 | 所有输⼊流 |
fputs | ⽂本⾏输出函数 | 所有输出流 |
fscanf | 格式化输⼊函数 | 所有输⼊流 |
fprintf | 格式化输出函数 | 所有输出流 |
fread | ⼆进制输⼊ | ⽂件输⼊流 |
fwrite | ⼆进制输出 | ⽂件输出流 |
当我们可以打开关闭文件了,就可以实现对文件里面顺序读写。
4.1fgetc
int fgetc ( FILE * stream );
这是读取获得单个字符
返回值:
成功后,将返回字符 read(提升为 int 值)。
返回类型为 int 以容纳特殊值 EOF,该值指示失败:
如果位置指示符位于文件末尾,则函数返回 EOF 并设置 stream 的 eof 指示符 (feof)。
如果发生其他读取错误,该函数还会返回 EOF,但会设置其错误指示符 (ferror)。
我们把之前的文件还原,在text.txt文件里写入abcdef,这里用fgetc函数来获取字符:
int main()
{//打开文件FILE* df = fopen("D:\\project\\text.txt","r");if (df == NULL){perror("FILE::");return 1;}else{printf("访问成功!\n");}//读取int j = 0;j = fgetc(df);printf("%c", j);j = fgetc(df);printf("%c", j);j = fgetc(df);printf("%c", j);//关闭文件int i=fclose(df);if (i == 0){df = NULL;return 0;}elseperror("fclose::");return 0;
}
运行结果:
这样就获取字符成功了。
4.2fputc
int fputc ( int character, FILE * stream );
这里第一个参数是要写的字符,第二个参数是要写入的目标文件。
返回值:
On success, the character written is returned.
If a writing error occurs, EOF is returned and the error indicator (ferror) is set.如果成功,则返回所写的字符。
如果发生写错误,则返回EOF并设置错误指示器(error)。
我们也可以用代码来写一下,把访问方式改成"w",可以覆盖掉之前的数据:
//打开文件
FILE* df = fopen("D:\\project\\text.txt","w");
//输入字符
char j = 'a';
for (j = 'a'; j <= 'z'; j++)
{fputc(j, df);
}
这里用一个循环实现输入a到z,我们打开txt可以看到成功了:
4.3fputs
int fputs ( const char * str, FILE * stream );
fputs函数是输入一个字符串,可以把字符串输入到文件中。第一个参数是要输入的字符串,第二个参数是目标文件。
我们就直接上代码:
//打开文件
FILE* df = fopen("D:\\project\\text.txt","w");
fputs("hello world\n", df);
我们往之前的文件中写入hello world。运行程序后:
发现之前的被覆盖,成功的输入进去了新的字符串。
4.4fgets
char * fgets ( char * str, int num, FILE * stream );
fgets是读取获得一个字符串,第一个参数是要把读取到的字符串存到的字符数组,第二个是要读取的个数,第三个是目标文件。
我们也直接代码演示:
//打开文件
FILE* df = fopen("D:\\project\\text.txt","r");
//输出字符串
char arr[20];
fgets(arr, 5, df);
printf("%s", arr);
这里就是读取5个字符存到arr数组中,并且打印出来:
这里为什么只有四个字母,因为它还要加上一个\0,也算一个,所以是五个。
总结
今天先到这里,,剩下的下一篇再继续写。