以字符串的形式读写文件
fgets()读字符串函数
定义形式
用来从指定的文件中读取一个字符串(仅能读取一行字符),并保存到字符数组中。
char *fgets(char *str,int n, FILE *fp); //str为存储的字符数组,n为读取的字符数量
读取成功时,返回字符数组的首地址,即**str**的首地址;读取失败时,返回**NULL**。如果开始读取时,文件内部指针已经指向了文件末尾,那么将读取不到字符,返回**NULL**。
读取到的字符串中,末尾包含了**'\0'**结束符,即实际读取到的字符为**n-1**个。
用法
在读取**n-1**个字符之前,如果出现了换行,或者读到了文件末尾,读取结束。
/* 一行一行地读取文件 */#include <stdio.h>#include <stdlib.h>#define N 100int main(void){FILE *fp;char str[N + 1];//判断文件打开是否失败if((fp = fopen("D:\\demo.txt","rt")) == NULL){puts("Fail to open file!\n");exit(0);}//连续读取多个字符while((str, N, fp) != NULL){printf("%s",str);}fclose(fp);return 0;}
fputs()写字符串函数
定义形式
用来向指定的文件写入一个字符串。
int fputs(char *str, FILE *fp); //str为待写入的字符串
用法
/* 向文件中追加一个字符串 */#include <stdio.h>#include <stdlib.h>#include <string.h>int main(void){FILE *fp;char str[102] = {0};char strTemp[100];//判断文件打开是否失败if((fp = fopen("D:\\demo.txt","rt")) == NULL){puts("Fail to open file!\n");exit(0);}printf("Input a string: ");gets(strTemp);strcat(str,"\n");strcat(str, strTemp); //字符串拼接fputs(str, fp); //写入字符串fclose(fp);return 0;}
以数据块的形式读写文件
Windows系统下,使用**fread()**函数和**fwrite()**函数时,应以二进制的形式打开。
fread()函数和fwrite()函数
定义形式
用来从指定文件中读写块数据。所谓块数据,可以是若干个字节的数据,可以是一个字符,也可以是一个字符串、多行数据,没有明确的限制。
/* fread()函数 */size_t fread(void *ptr,size_t size, size_t count, FILE *fp);/* fwrite()函数 */size_t fwrite(void *ptr,size_t size, size_t count, FILE *fp);其中,1. ptr:为用来存放数据的内存区块的指针,可以是数组、变量或结构体等;2. size:表示每个数据块的字节数;3. count:表示要读写的数据块的块数;4. fp:表示文件指针;5. 理论上,每次读写size*count个字节的数据。
- 读取成功时,返回读取的块数,即
count; - 如果返回值小于
count:
define N 5
int main(){ //从键盘输入的数据放入a,从文件读取的数据放入b int a[N], b[N]; int i, size = sizeof(int); FILE *fp; if( (fp=fopen(“D:\demo.txt”, “rb+”)) == NULL ){ //以二进制方式打开 puts(“Fail to open file!”); exit(0); }
//从键盘输入数据 并保存到数组afor(i=0; i<N; i++){scanf("%d", &a[i]);}//将数组a的内容写入到文件fwrite(a, size, N, fp);//将文件中的位置指针重新定位到文件开头rewind(fp);//从文件读取内容并保存到数组bfread(b, size, N, fp);//在屏幕上显示数组b的内容for(i=0; i<N; i++){printf("%d ", b[i]);}printf("\n");fclose(fp);return 0;
}
<a name="cdukH"></a>#### 示例2```c/* 从键盘输入两个学生数据,写入一个文件中,再读出这两个学生的数据 */#include<stdio.h>#include <stdlib.h>#define N 2struct stu{char name[10]; //姓名int num; //学号int age; //年龄float score; //成绩}boya[N], boyb[N], *pa, *pb;int main(){FILE *fp;int i;pa = boya;pb = boyb;if( (fp=fopen("d:\\demo.txt", "wb+")) == NULL ){puts("Fail to open file!");exit(0);}//从键盘输入数据printf("Input data:\n");for(i=0; i<N; i++,pa++){scanf("%s %d %d %f",pa->name, &pa->num,&pa->age, &pa->score);}//将数组 boya 的数据写入文件fwrite(boya, sizeof(struct stu), N, fp);//将文件指针重置到文件开头rewind(fp);//从文件读取数据并保存到数据 boybfread(boyb, sizeof(struct stu), N, fp);//输出数组 boyb 中的数据for(i=0; i<N; i++,pb++){printf("%s %d %d %f\n", pb->name, pb->num, pb->age, pb->score);}fclose(fp);return 0;}
格式化读写文件
fscanf()函数fprintf()函数
定义形式
fscanf()函数fprintf()函数与scanf()函数和printf()函数功能相似,都是格式化读写函数,但前者的读写对象是是磁盘文件,而不是键盘和显示器。
//fscanf()定义(读取文件):int fscanf(FILE *fp, char *format, ...); //format为格式控制字符串//fprintf()定义(写入文件):int fprintf(FILE *fp, char *format, ...);
用法
/* 用 fscanf() 和 fprintf() 函数来完成对学生信息的读写 */#include <stdio.h>#include <stdlib.h>#define N 2struct stu{char name[10];int num;int age;float score;};int main(void){struct stu boya[N],boyb[N],*pa,*pb;FILE *fp;pa = boya;pb = boyb;//判断文件是否打开失败if((fp = fopen("D:\\demo.txt","wt+")) == NULL){puts("Fail to open file!\n");exit(0);}//输入数据----boyaprintf("Input data:\n");for(int i = 0; i < N; i++,pa++){scanf("%s %d %d %f", pa->name, &pa->num, &pa->age, &pa->score);}pa = boya;//将boya的数据写入文件for(int i = 0; i < N;i++,pa++){fprintf(fp,"%s %d %d %f\n", pa->name, pa->num, pa->age, pa->score);}//重置文件指针rewind(fp);//输入数据----boybfor(i = 0; i < N; i++,pb++){fscanf(fp, "%s %d %d %f\n", pb->name, &pb->num, &pb->age, &pb->score);}pb = boyb;//将boyb的数据写入文件for(i=0; i<N; i++,pb++){printf("%s %d %d %f\n", pb->name, pb->num, pb->age, pb->score);}fcolse(fp);return 0;}
随机读写文件
通过移动文件内部的位置指针,再进行读写的方式,称之为随机读写。即从文件的任意位置开始读写。
实现随机读写的关键,是要按要求移动位置指针,这称为文件的定位。
文件定位函数rewind()和fseek()
定义形式
rewind()函数
**rewind()**函数用来将位置指针移动到文件开头。
void rewind(FILE *fp);
fseek()函数
**fseek()**函数用来将位置指针移动到任意位置。
int fseek(FILE *fp, long offset, int origin);其中,1. offset为偏移量,也就是要移动的字节数。offset为正时,向后移动,offset为负时,向前移动;2. origin为起始位置,也就是从何处开始计算偏移量。
C语言规定的起始位置有三种,分别为文件开头、当前位置和文件末尾。
| 起始点 | 常量名 | 常量值 |
|---|---|---|
| 文件开头 | SEEK_SET | 0 |
| 当前位置 | SEEK_CUR | 1 |
| 文件末尾 | SEEK_END | 2 |
**fseek()**函数一般用于二进制文件,在文本文件中,由于要进行转换,计算的位置有时会出错。
用法
在移动位置指针后,就可以用任一种读写函数进行读写了。由于是二进制文件,通常使用**fread()**函数和**fwrite()**函数进行读写。
/* 从键盘输入三组学生信息,保存到文件中,然后读取第二个学生的信息 */#include <stdio.h>#include <stdlib.h>#define N 3struct stu{char name[10];int num;int age;float score;};int main(void){struct stu boys[N], boy, *pboys;FILE *fp;pboys = boys;//判断文件打开是否失败if ((fp = fopen("D:\\demo.txt", "wb+")) == NULL){puts("Fail to open file!\n");exit(0);}printf("Input data:\n");// 记录学生信息for (int i = 0; i < N; i++, pboys++){scanf("%s %d %d %f", pboys->name, &pboys->num, &pboys->age, &pboys->score);}fwrite(boys, sizeof(struct stu), N, fp); //将学生信息写入文件fseek(fp, sizeof(struct stu), SEEK_SET); //移动向后一个结构体空间的位置指针fread(&boy, sizeof(struct stu), 1, fp); //读第二个学生信息printf("%s %d %d %f", boy.name, boy.num, boy.age, boy.score); //打印学生信息fclose(fp); //关闭文件return 0;}
C语言实现文件复制功能
主要思路
开辟一个缓冲区,不断从原文件中读取内容到缓冲区,每读取完一次,就将缓冲区中的内容写入到新建的文件,直到把原文件的内容读取完。需要注意的是,**fopen()**一定要以二进制的形式打开文件,否则系统会对文件进行一些处理,可能会导致复制后的文件出错。
实现
/* 实现复制文件功能 */#include <stdio.h>#include <stdlib.h>#define BUF_LEN 1024 * 4 //4k对齐空间int copyFile(char *fileRead, char *fileWrite);int main(void){char fileRead[100]; //要复制的文件名char fileWrite[100]; //复制后的文件名// 获取用户输入printf("要复制的文件:");scanf("%s", fileRead);printf("将文件复制到:");scanf("%s", fileWrite);// 进行复制操作if (copyFile(fileRead, fileWrite)){printf("恭喜你,文件复制成功!\n");}else{printf("文件复制失败!\n");}return 0;}/*** 文件复制函数* @param fileRead 要复制的文件的路径* @param fileWrite 复制后文件的保存路径* @return int 1: 复制成功;2: 复制失败**/int copyFile(char *fileRead, char *fileWrite){FILE *fpRead; //需要读取复制的文件指针FILE *fpWrite; //需要写入的文件的指针char *buffer = (char *)malloc(BUF_LEN); //开辟动态缓冲区int readCount; //实际读取到的字节数// 判断打开需要读写的文件是否失败if (((fpRead = fopen(fileRead, "rb")) == NULL) || ((fpRead = fopen(fileWrite, "wb")) == NULL)){printf("Cannot open file, press any key to exit!\n");getchar();exit(1);}// 复制文件while ((readCount = fread(buffer, 1, BUF_LEN, fpRead)) > 0){fwrite(buffer, readCount, 1, fpWrite);}free(buffer); //释放动态内存fclose(fpRead); //关闭文件fclose(fpWrite); //关闭文件return 1;}
获取文件大小
C语言没有提供获取文件大小的函数,只能通过自己编写函数实现。
ftell()函数
定义形式
用来获取文件内部位置指针距离文件开头的字节数。
long int ftell(FILE *fp);
**fp**要以二进制方式打开。如果以文本方式打开,函数的返回值可能没有意义。
用法
/* 实现获取文件大小功能 */#include <stdio.h>#include <stdlib.h>long fsize(FILE *fp);int main(void){long size = 0;FILE *fp = NULL;char filename[30] = "D:\\1.mp4";if ((fp = fopen(filename, "rb")) == NULL){ //以二进制方式打开文件printf("Failed to open %s...", filename);getch();exit(EXIT_SUCCESS);}printf("%ld\n", fsize(fp));return 0;}long fsize(FILE *fp){long n = 0; //fpos_t fpos; //记录当前指针位置fgetpos(fp, &fpos); //读取当前指针位置fseek(fp, 0, SEEK_END); //移动位置指针到文件末尾n = ftell(fp); //获取文件长度大小fsetpos(fp, &fpos); //恢复之前的指针位置return n;}
插入、删除和更改文件内容
文件类型
- 顺序文件(最常见的文件类型,文件内容按从头到尾的顺序依次存储在磁盘);
- 索引文件;
- 散列文件。
C语言没有提供插入、删除和更改文件内容的函数,只能通过自己编写函数实现。
文件复制函数
在数据的插入删除过程中,需要多次复制文件内容,我们有必要将该功能实现为一个函数。
/*** 文件复制函数* @param fSource 要复制的原文件* @param offsetSource 原文件的位置偏移(相对文件开头),也就是从哪里开始复制* @param len 要复制的内容长度,小于0表示复制offsetSource后边的所有内容* @param fTarget 目标文件,也就是将文件复制到哪里* @param offsetTarget 目标文件的位置偏移,也就是复制到目标文件的什么位置* @return 成功复制的字节数**/long fcopy(FILE *fSource, long offsetSource, long len, FILE *fTarget, long offsetTarget){int bufferLen = 1024*4; // 缓冲区长度char *buffer = (char*)malloc(bufferLen); // 开辟缓存int readCount; // 每次调用fread()读取的字节数long nBytes = 0; //总共复制了多少个字节int n = 0; //需要调用多少次fread()函数int i; //循环控制变量fseek(fSource, offsetSource, SEEK_SET);fseek(fTarget, offsetTarget, SEEK_SET);if(len<0){ //复制所有内容while( (readCount=fread(buffer, 1, bufferLen, fSource)) > 0 ){nBytes += readCount;fwrite(buffer, readCount, 1, fTarget);}}else{ //复制len个字节的内容n = (int)ceil((double)((double)len/bufferLen));for(i=1; i<=n; i++){if(len-nBytes < bufferLen){ bufferLen = len-nBytes; }readCount = fread(buffer, 1, bufferLen, fSource);fwrite(buffer, readCount, 1, fTarget);nBytes += readCount;}}fflush(fTarget);free(buffer);return nBytes;}
插入数据
主要思路
- 创建一个临时文件,将插入位置后面的内容复制到临时文件;
- 将原文件的内部位置指针移动到插入位置,写入插入内容;
-
实现
/*** 向文件中插入内容* @param fp 要插入内容的文件* @param buffer 缓冲区,也就是要插入的内容* @param offset 偏移量(相对文件开头),也就是从哪里开始插入* @param len 要插入的内容长度* @return 成功插入的字节数**/int finsert(FILE *fp, long offset, void *buffer, int len){long fileSize = fsize(fp);FILE *fpTemp; //临时文件if(offset>fileSize || offset<0 || len<0){ //插入错误return -1;}if(offset == fileSize){ //在文件末尾插入fseek(fp, offset, SEEK_SET);if(!fwrite(buffer, len, 1, fp)){return -1;}}if(offset < fileSize){ //从开头或者中间位置插入fpTemp = tmpfile();fcopy(fp, 0, offset, fpTemp, 0);fwrite(buffer, len, 1, fpTemp);fcopy(fp, offset, -1, fpTemp, offset+len);freopen(FILENAME, "wb+", fp );fcopy(fpTemp, 0, -1, fp, 0);fclose(fpTemp);}return 0;}
删除数据
主要思路
创建一个临时文件,将删除位置前面的所有内容复制到临时文件,再将删除位置后面的所有内容复制到临时文件;
- 将原文件删除,并创建一个新的同名文件;
-
实现
int fdelete(FILE *fp, long offset, int len){long fileSize = getFileSize(fp);FILE *fpTemp;if(offset>fileSize || offset<0 || len<0){ //错误return -1;}fpTemp = tmpfile();fcopy(fp, 0, offset, fpTemp, 0); //将前offset字节的数据复制到临时文件fcopy(fp, offset+len, -1, fpTemp, offset); //将offset+len之后的所有内容都复制到临时文件freopen(FILENAME, "wb+", fp ); //重新打开文件fcopy(fpTemp, 0, -1, fp, 0);fclose(fpTemp);return 0;}
更改文件内容
主要思路
修改数据时,如果新数据和旧数据长度相同,那么设置好内部指针,直接写入即可;
- 如果新数据比旧数据长,相当于增加新内容,思路和插入数据类似;
- 如果新数据比旧数据短,相当于减少内容,思路和删除数据类似。
实现
// 略。
