以字符串的形式读写文件

fgets()读字符串函数

定义形式

用来从指定的文件中读取一个字符串(仅能读取一行字符),并保存到字符数组中

  1. char *fgets(char *str,int n, FILE *fp); //str为存储的字符数组,n为读取的字符数量

读取成功时,返回字符数组的首地址,即**str**的首地址;读取失败时,返回**NULL**。如果开始读取时,文件内部指针已经指向了文件末尾,那么将读取不到字符,返回**NULL**
读取到的字符串中,末尾包含了**'\0'**结束符,即实际读取到的字符为**n-1**

用法

在读取**n-1**个字符之前,如果出现了换行,或者读到了文件末尾,读取结束

  1. /* 一行一行地读取文件 */
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #define N 100
  5. int main(void)
  6. {
  7. FILE *fp;
  8. char str[N + 1];
  9. //判断文件打开是否失败
  10. if((fp = fopen("D:\\demo.txt","rt")) == NULL)
  11. {
  12. puts("Fail to open file!\n");
  13. exit(0);
  14. }
  15. //连续读取多个字符
  16. while((str, N, fp) != NULL)
  17. {
  18. printf("%s",str);
  19. }
  20. fclose(fp);
  21. return 0;
  22. }

fputs()写字符串函数

定义形式

用来向指定的文件写入一个字符串

  1. int fputs(char *str, FILE *fp); //str为待写入的字符串

写入成功时,返回非负数;写入失败时,返回**EOF**

用法

  1. /* 向文件中追加一个字符串 */
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. int main(void)
  6. {
  7. FILE *fp;
  8. char str[102] = {0};
  9. char strTemp[100];
  10. //判断文件打开是否失败
  11. if((fp = fopen("D:\\demo.txt","rt")) == NULL)
  12. {
  13. puts("Fail to open file!\n");
  14. exit(0);
  15. }
  16. printf("Input a string: ");
  17. gets(strTemp);
  18. strcat(str,"\n");
  19. strcat(str, strTemp); //字符串拼接
  20. fputs(str, fp); //写入字符串
  21. fclose(fp);
  22. return 0;
  23. }

以数据块的形式读写文件

Windows系统下,使用**fread()**函数和**fwrite()**函数时,应以二进制的形式打开

fread()函数和fwrite()函数

定义形式

用来从指定文件中读写块数据。所谓块数据,可以是若干个字节的数据,可以是一个字符,也可以是一个字符串、多行数据,没有明确的限制。

  1. /* fread()函数 */
  2. size_t fread(void *ptr,size_t size, size_t count, FILE *fp);
  3. /* fwrite()函数 */
  4. size_t fwrite(void *ptr,size_t size, size_t count, FILE *fp);
  5. 其中,
  6. 1. ptr:为用来存放数据的内存区块的指针,可以是数组、变量或结构体等;
  7. 2. size:表示每个数据块的字节数;
  8. 3. count:表示要读写的数据块的块数;
  9. 4. fp:表示文件指针;
  10. 5. 理论上,每次读写size*count个字节的数据。
  • 读取成功时,返回读取的块数,即count
  • 如果返回值小于count
    • 对于fwrite()来说,肯定发生了写入错误,可以用ferror()函数检测;
    • 对于fread()来说,可能读到了文件末尾,或者发生了错误,可以用ferror()函数或feof()函数检测。

      用法

      示例1

      ```c / 从键盘输入一个数组,将数组写入文件再读取出来 /

      include

      include

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); }

  1. //从键盘输入数据 并保存到数组a
  2. for(i=0; i<N; i++){
  3. scanf("%d", &a[i]);
  4. }
  5. //将数组a的内容写入到文件
  6. fwrite(a, size, N, fp);
  7. //将文件中的位置指针重新定位到文件开头
  8. rewind(fp);
  9. //从文件读取内容并保存到数组b
  10. fread(b, size, N, fp);
  11. //在屏幕上显示数组b的内容
  12. for(i=0; i<N; i++){
  13. printf("%d ", b[i]);
  14. }
  15. printf("\n");
  16. fclose(fp);
  17. return 0;

}

  1. <a name="cdukH"></a>
  2. #### 示例2
  3. ```c
  4. /* 从键盘输入两个学生数据,写入一个文件中,再读出这两个学生的数据 */
  5. #include<stdio.h>
  6. #include <stdlib.h>
  7. #define N 2
  8. struct stu{
  9. char name[10]; //姓名
  10. int num; //学号
  11. int age; //年龄
  12. float score; //成绩
  13. }boya[N], boyb[N], *pa, *pb;
  14. int main(){
  15. FILE *fp;
  16. int i;
  17. pa = boya;
  18. pb = boyb;
  19. if( (fp=fopen("d:\\demo.txt", "wb+")) == NULL ){
  20. puts("Fail to open file!");
  21. exit(0);
  22. }
  23. //从键盘输入数据
  24. printf("Input data:\n");
  25. for(i=0; i<N; i++,pa++){
  26. scanf("%s %d %d %f",pa->name, &pa->num,&pa->age, &pa->score);
  27. }
  28. //将数组 boya 的数据写入文件
  29. fwrite(boya, sizeof(struct stu), N, fp);
  30. //将文件指针重置到文件开头
  31. rewind(fp);
  32. //从文件读取数据并保存到数据 boyb
  33. fread(boyb, sizeof(struct stu), N, fp);
  34. //输出数组 boyb 中的数据
  35. for(i=0; i<N; i++,pb++){
  36. printf("%s %d %d %f\n", pb->name, pb->num, pb->age, pb->score);
  37. }
  38. fclose(fp);
  39. return 0;
  40. }

格式化读写文件

fscanf()函数fprintf()函数

定义形式

fscanf()函数fprintf()函数与scanf()函数和printf()函数功能相似,都是格式化读写函数,但前者的读写对象是是磁盘文件,而不是键盘和显示器。

  1. //fscanf()定义(读取文件):
  2. int fscanf(FILE *fp, char *format, ...); //format为格式控制字符串
  3. //fprintf()定义(写入文件):
  4. int fprintf(FILE *fp, char *format, ...);

用法

  1. /* 用 fscanf() 和 fprintf() 函数来完成对学生信息的读写 */
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #define N 2
  5. struct stu
  6. {
  7. char name[10];
  8. int num;
  9. int age;
  10. float score;
  11. };
  12. int main(void)
  13. {
  14. struct stu boya[N],boyb[N],*pa,*pb;
  15. FILE *fp;
  16. pa = boya;
  17. pb = boyb;
  18. //判断文件是否打开失败
  19. if((fp = fopen("D:\\demo.txt","wt+")) == NULL)
  20. {
  21. puts("Fail to open file!\n");
  22. exit(0);
  23. }
  24. //输入数据----boya
  25. printf("Input data:\n");
  26. for(int i = 0; i < N; i++,pa++)
  27. {
  28. scanf("%s %d %d %f", pa->name, &pa->num, &pa->age, &pa->score);
  29. }
  30. pa = boya;
  31. //将boya的数据写入文件
  32. for(int i = 0; i < N;i++,pa++)
  33. {
  34. fprintf(fp,"%s %d %d %f\n", pa->name, pa->num, pa->age, pa->score);
  35. }
  36. //重置文件指针
  37. rewind(fp);
  38. //输入数据----boyb
  39. for(i = 0; i < N; i++,pb++){
  40. fscanf(fp, "%s %d %d %f\n", pb->name, &pb->num, &pb->age, &pb->score);
  41. }
  42. pb = boyb;
  43. //将boyb的数据写入文件
  44. for(i=0; i<N; i++,pb++)
  45. {
  46. printf("%s %d %d %f\n", pb->name, pb->num, pb->age, pb->score);
  47. }
  48. fcolse(fp);
  49. return 0;
  50. }

随机读写文件

通过移动文件内部的位置指针,再进行读写的方式,称之为随机读写。即从文件的任意位置开始读写
实现随机读写的关键,是要按要求移动位置指针,这称为文件的定位

文件定位函数rewind()和fseek()

定义形式

rewind()函数

**rewind()**函数用来将位置指针移动到文件开头

  1. void rewind(FILE *fp);

fseek()函数

**fseek()**函数用来将位置指针移动到任意位置

  1. int fseek(FILE *fp, long offset, int origin);
  2. 其中,
  3. 1. offset为偏移量,也就是要移动的字节数。offset为正时,向后移动,offset为负时,向前移动;
  4. 2. origin为起始位置,也就是从何处开始计算偏移量。

C语言规定的起始位置有三种,分别为文件开头、当前位置和文件末尾

起始点 常量名 常量值
文件开头 SEEK_SET 0
当前位置 SEEK_CUR 1
文件末尾 SEEK_END 2

**fseek()**函数一般用于二进制文件在文本文件中,由于要进行转换,计算的位置有时会出错

用法

移动位置指针后,就可以用任一种读写函数进行读写了。由于是二进制文件,通常使用**fread()**函数和**fwrite()**函数进行读写

  1. /* 从键盘输入三组学生信息,保存到文件中,然后读取第二个学生的信息 */
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #define N 3
  5. struct stu
  6. {
  7. char name[10];
  8. int num;
  9. int age;
  10. float score;
  11. };
  12. int main(void)
  13. {
  14. struct stu boys[N], boy, *pboys;
  15. FILE *fp;
  16. pboys = boys;
  17. //判断文件打开是否失败
  18. if ((fp = fopen("D:\\demo.txt", "wb+")) == NULL)
  19. {
  20. puts("Fail to open file!\n");
  21. exit(0);
  22. }
  23. printf("Input data:\n");
  24. // 记录学生信息
  25. for (int i = 0; i < N; i++, pboys++)
  26. {
  27. scanf("%s %d %d %f", pboys->name, &pboys->num, &pboys->age, &pboys->score);
  28. }
  29. fwrite(boys, sizeof(struct stu), N, fp); //将学生信息写入文件
  30. fseek(fp, sizeof(struct stu), SEEK_SET); //移动向后一个结构体空间的位置指针
  31. fread(&boy, sizeof(struct stu), 1, fp); //读第二个学生信息
  32. printf("%s %d %d %f", boy.name, boy.num, boy.age, boy.score); //打印学生信息
  33. fclose(fp); //关闭文件
  34. return 0;
  35. }

C语言实现文件复制功能

主要思路

开辟一个缓冲区,不断从原文件中读取内容到缓冲区,每读取完一次,就将缓冲区中的内容写入到新建的文件,直到把原文件的内容读取完。需要注意的是,**fopen()**一定要以二进制的形式打开文件,否则系统会对文件进行一些处理,可能会导致复制后的文件出错

实现

  1. /* 实现复制文件功能 */
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #define BUF_LEN 1024 * 4 //4k对齐空间
  5. int copyFile(char *fileRead, char *fileWrite);
  6. int main(void)
  7. {
  8. char fileRead[100]; //要复制的文件名
  9. char fileWrite[100]; //复制后的文件名
  10. // 获取用户输入
  11. printf("要复制的文件:");
  12. scanf("%s", fileRead);
  13. printf("将文件复制到:");
  14. scanf("%s", fileWrite);
  15. // 进行复制操作
  16. if (copyFile(fileRead, fileWrite))
  17. {
  18. printf("恭喜你,文件复制成功!\n");
  19. }
  20. else
  21. {
  22. printf("文件复制失败!\n");
  23. }
  24. return 0;
  25. }
  26. /**
  27. * 文件复制函数
  28. * @param fileRead 要复制的文件的路径
  29. * @param fileWrite 复制后文件的保存路径
  30. * @return int 1: 复制成功;2: 复制失败
  31. **/
  32. int copyFile(char *fileRead, char *fileWrite)
  33. {
  34. FILE *fpRead; //需要读取复制的文件指针
  35. FILE *fpWrite; //需要写入的文件的指针
  36. char *buffer = (char *)malloc(BUF_LEN); //开辟动态缓冲区
  37. int readCount; //实际读取到的字节数
  38. // 判断打开需要读写的文件是否失败
  39. if (((fpRead = fopen(fileRead, "rb")) == NULL) || ((fpRead = fopen(fileWrite, "wb")) == NULL))
  40. {
  41. printf("Cannot open file, press any key to exit!\n");
  42. getchar();
  43. exit(1);
  44. }
  45. // 复制文件
  46. while ((readCount = fread(buffer, 1, BUF_LEN, fpRead)) > 0)
  47. {
  48. fwrite(buffer, readCount, 1, fpWrite);
  49. }
  50. free(buffer); //释放动态内存
  51. fclose(fpRead); //关闭文件
  52. fclose(fpWrite); //关闭文件
  53. return 1;
  54. }

获取文件大小

C语言没有提供获取文件大小的函数,只能通过自己编写函数实现

ftell()函数

定义形式

用来获取文件内部位置指针距离文件开头的字节数

  1. long int ftell(FILE *fp);

**fp**要以二进制方式打开。如果以文本方式打开,函数的返回值可能没有意义。

用法

  1. /* 实现获取文件大小功能 */
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. long fsize(FILE *fp);
  5. int main(void)
  6. {
  7. long size = 0;
  8. FILE *fp = NULL;
  9. char filename[30] = "D:\\1.mp4";
  10. if ((fp = fopen(filename, "rb")) == NULL)
  11. { //以二进制方式打开文件
  12. printf("Failed to open %s...", filename);
  13. getch();
  14. exit(EXIT_SUCCESS);
  15. }
  16. printf("%ld\n", fsize(fp));
  17. return 0;
  18. }
  19. long fsize(FILE *fp)
  20. {
  21. long n = 0; //
  22. fpos_t fpos; //记录当前指针位置
  23. fgetpos(fp, &fpos); //读取当前指针位置
  24. fseek(fp, 0, SEEK_END); //移动位置指针到文件末尾
  25. n = ftell(fp); //获取文件长度大小
  26. fsetpos(fp, &fpos); //恢复之前的指针位置
  27. return n;
  28. }

插入、删除和更改文件内容

文件类型

  • 顺序文件最常见的文件类型,文件内容按从头到尾的顺序依次存储在磁盘);
  • 索引文件;
  • 散列文件。

C语言没有提供插入、删除和更改文件内容的函数,只能通过自己编写函数实现

文件复制函数

在数据的插入删除过程中,需要多次复制文件内容,我们有必要将该功能实现为一个函数。

  1. /**
  2. * 文件复制函数
  3. * @param fSource 要复制的原文件
  4. * @param offsetSource 原文件的位置偏移(相对文件开头),也就是从哪里开始复制
  5. * @param len 要复制的内容长度,小于0表示复制offsetSource后边的所有内容
  6. * @param fTarget 目标文件,也就是将文件复制到哪里
  7. * @param offsetTarget 目标文件的位置偏移,也就是复制到目标文件的什么位置
  8. * @return 成功复制的字节数
  9. **/
  10. long fcopy(FILE *fSource, long offsetSource, long len, FILE *fTarget, long offsetTarget){
  11. int bufferLen = 1024*4; // 缓冲区长度
  12. char *buffer = (char*)malloc(bufferLen); // 开辟缓存
  13. int readCount; // 每次调用fread()读取的字节数
  14. long nBytes = 0; //总共复制了多少个字节
  15. int n = 0; //需要调用多少次fread()函数
  16. int i; //循环控制变量
  17. fseek(fSource, offsetSource, SEEK_SET);
  18. fseek(fTarget, offsetTarget, SEEK_SET);
  19. if(len<0){ //复制所有内容
  20. while( (readCount=fread(buffer, 1, bufferLen, fSource)) > 0 ){
  21. nBytes += readCount;
  22. fwrite(buffer, readCount, 1, fTarget);
  23. }
  24. }else{ //复制len个字节的内容
  25. n = (int)ceil((double)((double)len/bufferLen));
  26. for(i=1; i<=n; i++){
  27. if(len-nBytes < bufferLen){ bufferLen = len-nBytes; }
  28. readCount = fread(buffer, 1, bufferLen, fSource);
  29. fwrite(buffer, readCount, 1, fTarget);
  30. nBytes += readCount;
  31. }
  32. }
  33. fflush(fTarget);
  34. free(buffer);
  35. return nBytes;
  36. }

插入数据

主要思路

  • 创建一个临时文件,将插入位置后面的内容复制到临时文件;
  • 将原文件的内部位置指针移动到插入位置,写入插入内容;
  • 再将临时文件中的内容写入原来的文件

    实现

    1. /**
    2. * 向文件中插入内容
    3. * @param fp 要插入内容的文件
    4. * @param buffer 缓冲区,也就是要插入的内容
    5. * @param offset 偏移量(相对文件开头),也就是从哪里开始插入
    6. * @param len 要插入的内容长度
    7. * @return 成功插入的字节数
    8. **/
    9. int finsert(FILE *fp, long offset, void *buffer, int len){
    10. long fileSize = fsize(fp);
    11. FILE *fpTemp; //临时文件
    12. if(offset>fileSize || offset<0 || len<0){ //插入错误
    13. return -1;
    14. }
    15. if(offset == fileSize){ //在文件末尾插入
    16. fseek(fp, offset, SEEK_SET);
    17. if(!fwrite(buffer, len, 1, fp)){
    18. return -1;
    19. }
    20. }
    21. if(offset < fileSize){ //从开头或者中间位置插入
    22. fpTemp = tmpfile();
    23. fcopy(fp, 0, offset, fpTemp, 0);
    24. fwrite(buffer, len, 1, fpTemp);
    25. fcopy(fp, offset, -1, fpTemp, offset+len);
    26. freopen(FILENAME, "wb+", fp );
    27. fcopy(fpTemp, 0, -1, fp, 0);
    28. fclose(fpTemp);
    29. }
    30. return 0;
    31. }

    删除数据

    主要思路

  • 创建一个临时文件,将删除位置前面的所有内容复制到临时文件,再将删除位置后面的所有内容复制到临时文件;

  • 将原文件删除,并创建一个新的同名文件;
  • 再将临时文件中的内容复制到新创建的文件

    实现

    1. int fdelete(FILE *fp, long offset, int len){
    2. long fileSize = getFileSize(fp);
    3. FILE *fpTemp;
    4. if(offset>fileSize || offset<0 || len<0){ //错误
    5. return -1;
    6. }
    7. fpTemp = tmpfile();
    8. fcopy(fp, 0, offset, fpTemp, 0); //将前offset字节的数据复制到临时文件
    9. fcopy(fp, offset+len, -1, fpTemp, offset); //将offset+len之后的所有内容都复制到临时文件
    10. freopen(FILENAME, "wb+", fp ); //重新打开文件
    11. fcopy(fpTemp, 0, -1, fp, 0);
    12. fclose(fpTemp);
    13. return 0;
    14. }

    更改文件内容

    主要思路

  • 修改数据时,如果新数据和旧数据长度相同,那么设置好内部指针,直接写入即可;

  • 如果新数据比旧数据长,相当于增加新内容,思路和插入数据类似;
  • 如果新数据比旧数据短,相当于减少内容,思路和删除数据类似

    实现

    1. // 略。