文件操作
文件操作
山林川泽文件操作
文件操作的实现将帮助我们把数据存储到文件中,既硬盘上的文件,如我们所熟知的txt格式,或其他各种后缀的文件,避免程序结束后数据丢失,实现存储数据的功能,甚至充当“数据库”的功能。
操作文件的步骤简单来说分为三步
第一步:打开文件
第二步:读写文件
第三步:关闭文件
第一步可以通过fopen函数来实现,这一步作用主要是建立程序和文件的关系,获取文件在内存中的文件指针,方便后面两步。
第二步读写文件分为fprintf、fscanf或者fwrite、fread或者fputs、getss等多组函数来实现。
每组函数都分别是写和读文件。就像我们熟知的printf和scanf这组输入输出文件一样,这不过这里的读写不是向屏幕,而是向文件。
第三步关闭文件则需要fclose函数实现。
这一步则是切断文件指针和文件的关联,避免误操作。如果未关闭文件就对文件进行读写删除等操作,就是出现类似“正在被使用,无法修改”的提示
在C语言中,对文件操作之前,首先需要打开文件,使用的函数是fopen函数,它的作用是打开文件,获取该文件的文件指针,方便后续操作。函数原型为:
1 | FILE *fopen(const char *filename, const char *mode); |
可以看得出来,该函数需要两个字符串类型的参数,第一个是文件名,既要操作的文件对象。第二个是打开方式,这里的打开方式只是,对文件以何种模式打开,包括文本模式打开还是二进制打开、是读还是写还是追加等等等等,具体类型如下表,可以根据情况使用:
参数 | 作用 |
---|---|
r | 以只读方式打开文件,该文件必须存在。 |
r+ | 以读/写方式打开文件,该文件必须存在。 |
rb+ | 以读/写方式打开一个二进制文件,只允许读/写数据。 |
rt+ | 以读/写方式打开一个文本文件,允许读和写。 |
w | 打开只写文件,若文件存在则文件长度清为零,即该文件内容会消失;若文件不存在则创建该文件。 |
w+ | 打开可读/写文件,若文件存在则文件长度清为零,即该文件内容会消失;若文件不存在则创建该文件。 |
a | 以附加的方式打开只写文件。若文件不存在,则会创建该文件;如果文件存在,则写入的数据会被加到文件尾后,即文件原先的内容会被保留(EOF 符保留)。 |
a+ | 以附加方式打开可读/写的文件。若文件不存在,则会创建该文件,如果文件存在,则写入的数据会被加到文件尾后,即文件原先的内容会被保留(EOF符不保留)。 |
wb | 以只写方式打开或新建一个二进制文件,只允许写数据。 |
wb+ | 以读/写方式打开或新建一个二进制文件,允许读和写。 |
wt+ | 以读/写方式打开或新建一个文本文件,允许读和写。 |
at+ | 以读/写方式打开一个文本文件,允许读或在文本末追加数据。 |
ab+ | 以读/写方式打开一个二进制文件,允许读或在文件末追加数据。 |
函数的返回值则表示打开成功后的文件指针,格式为FILE类型,是一个结构体类型,供后面使用,如果打开失败,则返回NULL。
比如:如果我们现在想打开一个D盘根目录下的abc.dat,并且想读出该文件里的数据,那么我们可以这样写:
1 | FILE *fp; |
注意事项
- 该文件的目录是绝对路径,因此这样写,如果不写盘符比如abc.dat则表示相对路径,表示与本程序同目录下。
- 路径中的反斜杠虽然只有一个,但这里打了两个,原因在于C语言字符串中对反斜杠要当作转义字符处理,因此要用两个反斜杠才能表示一个。
- 一旦以r也就是只读的方式打开文件,后面则不允许写数据,否则会出错,一定要保持一致!
下面来介绍fprintf函数,它的原型是:
1 | int fprintf (FILE* stream, const char*format, [argument]) |
该函数是一个格式化写入的库函数,可以看到,除了长得和printf函数很像以外,参数也非常像,多了一个第一个参数文件指针,即第一步打开文件时得到的文件指针,后面的参数和printf一样,按照指定的格式将数据写入文件。例如:
1 | fprintf(fp,"%s","www.dotcpp.com"); |
这句代码的意思是将字符串”www.dotcpp.com"以%s的格式写入fp所指向的文件中。
控制符 | 作用 |
---|---|
%c | 字符 |
%d 或 %i | 有符号十进制整数 |
%e | 使用 e 字符的科学科学记数法(尾数和指数) |
%E | 使用 E 字符的科学科学记数法(尾数和指数) |
%f | 十进制浮点数 |
%g | 自动选择 %e 或 %f 中合适的表示法 |
%G | 自动选择 %E 或 %f 中合适的表示法 |
%o | 有符号八进制 |
%s | 字符的字符串 |
%u | 无符号十进制整数 |
%x | 无符号十六进制整数 |
%X | 无符号十六进制整数(大写字母) |
%p | 指针地址 |
%n | 无输出 |
% | 字符 |
控制符 | 作用 |
---|---|
%c | 字符 |
%d 或 %i | 有符号十进制整数 |
%e | 使用 e 字符的科学科学记数法(尾数和指数) |
%E | 使用 E 字符的科学科学记数法(尾数和指数) |
%f | 十进制浮点数 |
%g | 自动选择 %e 或 %f 中合适的表示法 |
%G | 自动选择 %E 或 %f 中合适的表示法 |
%o | 有符号八进制 |
%s | 字符的字符串 |
%u | 无符号十进制整数 |
%x | 无符号十六进制整数 |
%X | 无符号十六进制整数(大写字母) |
%p | 指针地址 |
%n | 无输出 |
% | 字符 |
控制符 | 作用 |
---|---|
%c | 字符 |
%d 或 %i | 有符号十进制整数 |
%e | 使用 e 字符的科学科学记数法(尾数和指数) |
%E | 使用 E 字符的科学科学记数法(尾数和指数) |
%f | 十进制浮点数 |
%g | 自动选择 %e 或 %f 中合适的表示法 |
%G | 自动选择 %E 或 %f 中合适的表示法 |
%o | 有符号八进制 |
%s | 字符的字符串 |
%u | 无符号十进制整数 |
%x | 无符号十六进制整数 |
%X | 无符号十六进制整数(大写字母) |
%p | 指针地址 |
%n | 无输出 |
% | 字符 |
fprintf函数虽然和printf函数很像,表示输出,但准确说是是写入的意思,是指程序向文件里写,要清楚数据的流向。
如同printf与scanf的关系一样,fprintf和fscanf也是如此,fprintf负责向文件里写数据,fscanf函数则可以从文件里读数据,它的函数原型如下:
1 | int fscanf(FILE *stream, char *format[,argument...]); |
作为格式化写数据函数,它的参数同样比scanf也多一个参数,即第一个参数文件指针,表示读取的文件目标,其余参数和scanf一样,按照相应的格式进行读取,返回值表示读取数据的字节数。比如:
1 | char str[100]; |
则表示从fp所指向的文件中进行读数据,与空格或换行结束,将结果保存到str数组中。
更多fscanf的格式如下表:
格式 | 作用 |
---|---|
%d | 读入一个十进制整数 |
%i | 读入十进制,八进制,十六进制整数,与%d类似,但是在编译时通过数据前置或后置来区分进制,如加入“0x”则是十六进制,加入“0”则为八进制。例如串“031”使用%d时会被算作31,但是使用%i时会算作25 |
%u | 读入一个无符号十进制整数 |
%f %F %g %G | 用来输入实数,可以用小数形式或指数形式输入 |
%x %x | 读入十六进制整数 |
%o | 读入八进制整数 |
%s | 直到遇到一个空格字符(空格字符可以是空白、换行和制表符) |
%c | 单个字符:读取下一个字符。如果指定了一个不为 1 的宽度 width,函数会读取 width 个字符,并通过参数传递,把它们存储在数组中连续位置。在末尾不会追加空字符 |
fwrite函数就是写文件的函数,它的函数原型如下:
1 | size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) |
可以看到这个函数的参数有四个:
第一个ptr是要写入的数据的头指针,无符号类型;
第二个参数size是大小,表示每个写入元素的大小,单位是字节;
第三个参数nmemb是个数,以上一个参数为单位的个数;
第四个参数stream就是文件指针,表示往哪里写。
至于返回值,如果成功执行,则返回写入元素的个数,如果不和nmemb相等,则表示出错。
1 |
|
如代码所示,程序运行后,并不会在屏幕上有任何显示,而是将str中的字符串写入了dotcpp.dat这个文件中
与fwrite是一对,读文件的函数fread的函数原型如下:
1 | size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) |
fread函数的作用是从文件里读内容到程序中,它的参数意思是:
第一个参数ptr表示盛放内容的首地址;
第二个参数size表示每个元素的大小,单位还是字节;
第三个参数nmem表示要读取的元素个数;
第四个参数stream表示的是文件指针,即从哪个文件中读取。
返回值则是表示读取元素的个数,与nmemb一致表示读取成功,否则失败。
1 |
|