所谓文件一般都是指存储在外部介质上数据的集合,输入输出时数据传输的过程,即数据流。
根据数据的组织形式,数据文件可以分为ASCII文件和二进制文件,
数据在磁盘上的存储方式:字符文件一律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以用二进制形式存储。
每个被使用的文件都在内存中开辟一个相应的文件信息区,用来存放文件的相关信息(文件的名字、文件状态以及文件当前位置),这些信息是保存在一个结构体变量中的,FILE:
typedef struct {short level;//缓冲区满or空unsigned flags;//文件状态标志char fd;//文件描述符unsigned char hold;//如缓冲区无内容不读取字符short bsize;//缓冲区的大小unsigned char *buffer;//数据缓冲区的位置unsigned char *curp;//指针当前的指向unsigned istemp;//临时文件指示器short token;//用于有效性检查
} FILE;
每一个FILE类型变量对应一个文件的信息区,将指向文件信息区的指针称为文件指针。
注:指向文件的指针变量并不是指向外部介质上的数据文件的开头,而是指向内存中的文件信息区的开头。
所谓打开文件是指为文件建立相应的信息区(存放有关文件信息)与文件缓冲区(暂时存放输入输出数据)。
//常用的打开文件的方式
FILE *fp;
if ((fp = fopen("file.txt", "r")) == NULL) {printf("can not open this file\n");exit(0);
}
程序中可以使用3个标准的流文件:标准输入stdin、标准输出stdout、标准错误输出stderr
标准输入流是从终端的输入、标准输出流是向终端的输出、标准错误输出流是程序出错将错误信息发送到终端。
程序开始运行时系统自动打开这3个标准流文件,程序编写者直接使用即可。
关闭文件就是撤销文件信息区与文件缓冲区,使文件指着不再指向该文件。
如果不关闭文件将会丢失数据,因为向文件写入数据时是先将数据输入到缓冲区,待缓冲区满之后才会写入磁盘(缓冲区数据丢失)。
fclose(fp);
在顺序读写中,先写入的数据存放在文件中前面的位置,后写入的数据存放在文件中后面的位置。
#include
#includeint main() {FILE *fp;char ch;char filename[10];printf("please enter a filename:");scanf("%s", filename);if ((fp = fopen(filename, "w")) == NULL) {printf("can not open this file\n");exit(0);}ch = getchar();//用于接收最后输入的回车符printf("please enter a char array end with a '#':");ch = getchar();while (ch != '#') {fputc(ch, fp);//向磁盘文件写入一个字符ch = getchar();}fclose(fp);return 0;
}
成功通过fputc函数向test.txt文件中逐个字符写入,文件操作成功。
#include
#includeint main() {FILE *inputfp, *outputfp;char ch;char srcFileName[20], destFileName[20];printf("enter the input file name:");scanf("%s", srcFileName);printf("enter the output file name:");scanf("%s", destFileName);if ((inputfp = fopen(srcFileName, "r")) == NULL) {//打开输入文件printf("can not open this src file!\n");exit(0);}if ((outputfp = fopen(destFileName, "w")) == NULL) {//打开输出文件printf("can not open this dest file!\n");exit(0);}while (!feof(inputfp)) {//feof函数用于检测文件读写是否到达末尾ch = fgetc(inputfp);//src字符读取fputc(ch, outputfp);//dest字符写入}fclose(inputfp);fclose(outputfp);return 0;
}
注:fgets和fputs函数功能类似于gets和puts函数,只是gets和puts以终端为读写对象,而fgets和fputs以指定的文件为读写对象。
#include
#include
#includeint main() {FILE *fp;char str[3][20];char temp[20];int n = 3;printf("please enter your strings:\n");for (int i = 0; i < n; ++i) gets(str[i]);//从标准输入设备中获取字符串 并用str[i]指针对其指向//1.对字符串进行排序(直接选择排序)for (int i = 0; i < n - 1; ++i) {int max = i;for (int j = i + 1; j < n; ++j) {//寻找i及其之后的字符串中最大的字符串 并将其赋值给maxif (strcmp(str[max], str[j]) > 0) max = j;}if (max != i) {//将最大的字符串放在有序的位置strcpy(temp, str[i]);strcpy(str[i], str[max]);strcpy(str[max], temp);}}//2.打开文件if ((fp = fopen("string.txt", "w")) == NULL) {printf("can not open the file!\n");exit(0);}//3.写入数据for (int i = 0; i < n; ++i) {fputs(str[i], fp);fputs("\n", fp);}return 0;
}
string.txt
中将数据读会到字符串数组中,并将其打印在屏幕上:#include
#includeint main() {FILE *fp;char str[3][50];//1.打开文件if ((fp = fopen("string.txt", "r")) == NULL) {printf("can not open the file\n");exit(0);}//2.将文件中的数据读入数组int index = 0;while (fgets(str[index], 50, fp) != NULL) index++;//3.将数组中的数据进行打印for (int i = 0; i < 3; ++i) printf("%s", str[i]);fclose(fp);return 0;
}
注:fprintf和fscanf与printf和scanf的区别是其读写对象不是终端而是文件。
fprintf(文件指针, 格式字符串, 输出列表);
fscanf(文件指针, 格式字符串, 输入列表);
用fprintf和fscanf函数对磁盘文件进行读写使用方便,但是由于需要进行ACSII码与二进制形式之间的转换,花费时间较多。
因此在内存与磁盘进行频繁交换数据的情况下,最好不用。
而用fread与fwrite函数进行二进制的读写。
C语言允许使用fread函数从文件中读一个数据块,用fwrite函数向文件中写入一个数据块(读写以二进制形式进行)。
在向磁盘写入数据时,直接将内存中的数据原封不动的复制到磁盘文件上。
fread(buffer, size, count, fp);
fwrite(buffer, size, count, fp);
#include#define SIZE 5struct Student {char name[20];int num;int age;char addr[50];
};struct Student stu[SIZE];void bwrite() {FILE *fp;if ((fp = fopen("stu.txt", "wb")) == NULL) {printf("can not open the file\n");return;}for (int i = 0; i < SIZE; ++i) {if (fwrite(&stu[i], sizeof(struct Student), 1, fp) != 1) {printf("file write error\n");}}fclose(fp);
}void bread() {FILE *fp;if ((fp = fopen("stu.txt", "rb")) == NULL) {printf("can not open the file\n");return;}for (int i = 0; i < SIZE; ++i) {fread(&stu[i], sizeof(struct Student), 1, fp);}fclose(fp);
}int main() {printf("please enter data of the student:\n");for (int i = 0; i < SIZE; ++i) scanf("%s %d %d %s", stu[i].name, &stu[i].num, &stu[i].age, stu[i].addr);bwrite();printf("current data of the student:\n");bread();for (int i = 0; i < SIZE; ++i) printf("%-10s%4d%4d%10s\n", stu[i].name, stu[i].num, stu[i].age, stu[i].addr);return 0;
}
随机访问不是按数据在文件中的物理位置次序进行读写的,而是可以对任何位置上的数据进行访问,这种方法比顺序访问效率高很多。
为了对读写进行控制,系统为每个文件设置了一个文件读写位置标记(文件位置标记),
可以根据读写需要,人为的移动文件位置标记的位置,即随机读写。
rewind:作用是使文件位置标记重新指向文件开头,无返回值。
FILE *fp1, *fp2;
fp1 = fopen("src.txt", "r");
fp2 = fopen("dest.txt", "w");
while (!feof(fp1)) putchar(getc(fp1));//将文件内容输出到屏幕
rewind(fp1);
while (!feof(fp1)) putc(getc(fp1), fp2);//将文件内容拷贝到dest.txt
fclose(fp1);
fclose(fp2);
fseek:用于改变文件位置标记,fseek一般用于二进制文件,fseek(文件指针, 位移量, 起始点);
ftell:作用是得到流式文件中文件位置标记的当前位置,用相对于文件开头的位移量来表示。出错返回-1
FILE *fp;
if ((fp = fopen("stu.txt", "rb")) == NULL) {printf("can not open the file\n");exit(0);
}
for (int i = 0; i < SIZE; i += 2) {fseek(fp, i * sizeof(struct Student), 0);//移动文件位置标记fread(&stu[i], sizeof(struct Student), 1, fp);//读取一个数据块到结构体变量printf("%-10s%4d%4d%10s\n", stu[i].name, stu[i].num, stu[i].age, stu[i].addr);
}
fclose(fp);
ferror(fp)
在调用各种输入输出函数(putc、getc、fread、fwrite)时如果出现错误,除了函数返回值有所反映外,还可以使用ferror函数检查。
如果返回值为0表示没有读写错误,如果返回非零值表示读写出错。
注:在调用1个输出输出函数后,需要立即检查ferror函数的返回值,否则错误信息会丢失。
clearerr(fp)
clearerr函数的作用是使文件错误标志和文件结束标志置为0,
在输入输出函数出现错误ferror值为一个非零值时,应立即调用clearerror(fp)
,以便在进行下一次检查。
下一篇:yolov5模型训练流程