一.文件的概念
为什么使用文件?
如果没有文件,我们写的程序数据是存储在电脑的内存中的,如果程序退出,内存回收,数据就丢失了,等再次运行程序,是看不到上次程序的数据的,如果要将数据进行持久化的保存,我们可以使用文件。
什么是文件?
磁盘(硬盘)上的文件就是文件。但是在程序设计中,我们一般谈的文件有两种:程序文件
和数据文件(从文件功能的角度来分类的)。
**程序文件:**程序文件包括源程序文件(后缀为.c),目标文件(windows环境下为,obj),可执行程序(windows环境下为.exe)。
**数据文件:**文件的内容不一定是程序,而是程序运行时的读写数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。
二.二进制文件和文本文件
二进制文件:数据在内存中以二进制的形式存储,如果不加转换的输出到外存的文件中,就是二进制文件。
**文本文件:**如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件。
一个数据在文件中的存储:字符型数据在内存中都是以ASCII形式存储的,数值类型既可以字符类型数据的存储方式,也可以采用二进制的存储方式。
三.文件的打开和关闭
在学习文件的打开和关闭之前,我们得先了解流和标准流。
流
程序中数据需要输出到各种外部设备,也需要从各种外部设备输入数据,不同的外部设备的输入和输出的操作不相同,为了方便对于各种设备的操作,我们就抽象出了流这个概念。我们可以将流想象成流淌着的字符的河。
标准流: 在C语言程序启动的时候,默认打开了三个流。
stdin-标准输入流 ,在大多数的环境中,从键盘输入,scanf函数就是从你标准输入流中读取数据。
stdout-标准输出流 ,在大多数据环境下,输出至显示器界面,printf函数就是将信息输出到标准输出流当中。
stderr-标准错误流,大多数环境中输出到显示器界面。
以上这三个流的类型都是FILE*,通常称为文件指针(文件类型指针)。
C语言中就是通过FILE*的文件指针来维护流的各种操作的。通过文件指针就可以间接找到与他相关联的文件。
在文件读写之前,应该先打开文件,在结束之后关闭文件。
cpp//打开文件 FILE* fopen(contst char* filename, const char* mode); //关闭文件 FILE* fclose(const char* filename);
mode表示文件的打开模式,"r"(只读),"w"(只写),"a"(追加),"rb"(只读),"wb"(只写)等。
同时也要注意如果指定文件不存在的可能出错。
cpp#include <stdio.h> int main() { FILE* pfile; //打开文件 pfile = fopen("myfile.txt", "w"); //文件操作 if (pfile != NULL) { fputs("fopen example", pfile); fclose(pfile); } return 0; }
四.文件的顺序读写
fgetc------字符输入函数。(适用于所有输入流)
fputc------字符输出函数。(适用于所有输出流)
fgets------文本输入函数。(适用于所有输入流)
fputs------文本输出函数。(适用于所有输出流)
fscanf------格式化输入函数。(适用于所有输入流)
fprintf------格式化输出函数。(适用于所有输入流)
fread------二进制输入。(文件输入流)
fwrite------二进制输出。(文件输出流)
上面所说的适用于所有输入流指的是一般适用于标准输入流和其他输入流(如文件输入流);所有的输出流指的是一般适用于标准输出流和其他输出流(如文件输出流)。
五.文件的随机读写
1.fseek
根据文件指针的位置和偏移量来定位文件指针(文件内容的光标)。
cppint fseek(FILE* stream, long int offset, int origin);
例子:
cpp#include <stdio.h> int main() { FILE* pfile; pfile = fopen("example.txt", "wb"); fputs("This is an apple.", pfile); fseek(pfile, 9, SEEK_SET); fputs("sam", pfile); fclose(pfile); return 0; }
2. ftell
返回文件指针相对于起始位置的偏移量。
cpplong int ftell(FILE* stream);
例子:
cpp#include <stdio.h> int main() { FILE* pfile; long size; pfile = fopen("myfile.txt", "rb"); if (pfile == NULL) perror("error opening file"); else { fseek(pfile, 0, SEEK_END); size = ftell(pfile); fclose(pfile); printf("size of myfile.txt:%ld bytes.\n",size); } return 0; }
rewind
让指针的位置回到文件的起始位置。
cppvoid rewind(FILE*stream)
例子:
cpp#include <stdio.h> int main() { int n; FILE* pfile; char buffer[27]; pfile = fopen("myfile.text", "w+"); for (n = 'A'; n <= 'Z'; n++) fputc(n, pfile); rewind(pfile); fread(buffer, 1, 26, pfile); fclose(pfile); buffer[26] = '\0'; printf(buffer); return 0; }
六.文件读取结束
feof的作用
当文件读取结束的时候,判断读取结束的原因是否为:遇到文件末尾结束。
PS:不能用feof的返回值来判断文件是否结束。
1.文本文件是否读取结束
判断返回值是否为 EOF (fgetc)或者NULL(fgets)
例如:
fgetc 判断是否为EOF
fgets 判断是否为NULL.
eg:
cpp#include <stdio.h> #include <stdlib.h> int main() { int c; FILE* fp = fopen("test.txt", "r"); if (!fp) { perror("file opening failed"); return EXIT_FAILURE; } //fgetc当读取失败的时候或者遇到文件结束的时候,都会返回EOF while ((c = fgetc(fp) != EOF)) //标准C I/O读取循环 { putchar(c); } //判断是什么原因结束的 if (ferror(fp)) puts("I/O error when reading"); else if (feof(fp)) puts("End of file reached successfully"); fclose(fp); return 0; }
2.二进制文件的读取结束判断
判断返回值是否小于实际要读取的个数。
例如:
fread判断返回值是否小于实际要读取的个数。
eg:
cpp#include <stdio.h> enum { SIZE = 5 }; int main(void) { double a[SIZE] = { 1.,2.,3.,4.,5. }; FILE* fp = fopen("test.bin", "wb");//必须用二进制 fwrite(a, sizeof(*a), SIZE, fp); fclose(fp); double b[SIZE]; fp = fopen("test.bin", "rb"); size_t ret_code = fread(b, sizeof(*b), SIZE, fp);//读double 的数组 if (ret_code) { puts("Array read successfully ,contents:"); for (int i = 0; i < SIZE; i++) printf("%f ", b[i]); putchar('\n'); } else { if (feof(fp)) printf("error reading test .bin:unexpecxted end of file \n"); else if (ferror(fp)) perror("errror reading test.bin"); } fclose(fp); }
七.文件缓冲区
ANSIC标准采用 "缓冲文件系统" 处理数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块"文件缓冲区"。
从 内存 向 磁盘 输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。
如果从 磁盘 向 计算机 读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。
缓冲区的大小根据C编译系统决定的。
C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。