C语言--文件读写函数的使用
目录
一、前言
- 文件访问模式
- 流和标准流
二、顺序读写函数的介绍: fgetc、 fputc、 fgets、fputs、 fscanf、 fprintf、fread、 fwrite
三、应用:把一个文件的数据拷贝到另一个文件上
四、文件读取结束的判定
- 被错误使用的 feof
- 使用方法
五、总结
前言
文件访问模式
使用fopen来打开文件,就要了解关于文件的访问模式:


注意:
使用“r”打开文件,打开的文件必须存在,否则,程序出错
使用“w”打开文件,如果文件存在,并且文件中有数据,会清空数据。文件不存在,会建一个新文件
打开文件用fopen
FILE * fopen ( const char * filename, const char * mode );
有文件的打开就有文件的关闭,
关闭文件用fclose
int fclose ( FILE * stream );
具体操如下:
int main()
{FILE* pf = fopen("test.txt", "w");if (pf == NULL){perror("fopen");return;}//写文件//关闭文件fclose(pf);pf = NULL;return 0;
}
流和标准流
在学习读写函数前,先要了解流和标准流的概念
流
我们程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输⼊输出操作各不相同,为了⽅便程序员对各种设备进⾏⽅便的操作,我们抽象出了流的概念,我们可以把流想象成流淌着字符的河。
C程序针对⽂件、画⾯、键盘等的数据输⼊输出操作都是通过流操作的。
⼀般情况下,我们要想向流⾥写数据,或者从流中读取数据,都是要打开流,然后操作。
标准流
那为什么我们从键盘输⼊数据,向屏幕上输出数据,并没有打开流呢?
那是因为C语⾔程序在启动的时候,默认打开了3个流:
• stdin - 标准输⼊流,在⼤多数的环境中从键盘输⼊,scanf函数就是从标准输⼊流中读取数据。
• stdout - 标准输出流,⼤多数的环境中输出⾄显⽰器界⾯,printf函数就是将信息输出到标准输出
流中。
• stderr - 标准错误流,⼤多数环境中输出到显⽰器界⾯。
这是默认打开了这三个流,我们使⽤scanf、printf等函数就可以直接进⾏输⼊输出操作的。
stdin、stdout、stderr 三个流的类型是: FILE* ,通常称为⽂件指针。
C语⾔中,就是通过 FILE* 的⽂件指针来维护流的各种操作的。
fputc
把字符输出到流上
int fputc ( int character, FILE * stream );
参数类型
第一个参数:要输出的字符
第二个参数:流
使用方法
可以一个一个字符写,也可以使用循环
int main()
{FILE* pf = fopen("test.txt", "w");if (pf == NULL){perror("fopen fail");return;}//写文件/*fputc('a', pf);fputc('b', pf);fputc('c', pf);*/char ch = 0;for (ch = 'a'; ch <= 'z'; ch++){fputc(ch, pf);}//关闭文件fclose(pf);pf = NULL;return 0;
}
fgetc
从流中读取字符
int fgetc ( FILE * stream );
参数类型
流
返回值
成功读取返回读取到的字符;否则,返回EOF。
使用方法
如果打开失败进入if语句;perror用来显示 报错信息
int main()
{FILE* pf = fopen("test.txt", "r");if (pf == NULL){perror("fopen fail");return;}//读文件int ch = 0;while ((ch=fgetc(pf))!=EOF){printf("%c", ch);}//关闭文件fclose(pf);pf = NULL;return 0;
}
fputs
把字符串输出到流上
int fputs ( const char * str, FILE * stream );
参数类型
str:存储字符串的数组指针
stream:流
使用方法
int main()
{//打开文件FILE* pf = fopen("test.txt", "w");if (pf == NULL){perror("fopen");return;}//写文件fputs("hello world\n", pf);fputs("hello bit\n", pf);//关闭文件fclose(pf);pf = NULL;return 0;
}
fgets
从流中读取字符并将它们作为字符串存储到 str 中
char * fgets ( char * str, int num, FILE * stream );
参数类型
str:存储读取到的字符串,的数组指针
num:读取字符数
stream:流
返回值
读取成功,返回读取到的字符串。
否则,返回NULL
使用方法
注意:
fgets只读一行 实际读取num-1个字符,末尾要加\0
如果有多行数据 第一行的字符数不够 读完字符 长度允许再加\n, 末尾再补\0 就是会把\n(换行符)读进去
int main()
{//打开文件FILE* pf = fopen("test.txt", "r");if (pf == NULL){perror("fopen");return;}//读文件char arr[20] = { "0"};while (fgets(arr, 20, pf) != NULL){printf("%s", arr);}//fgets(arr, 20, pf);//只读一行 第一行的字符数够 实际只读9个字符,末尾要加\0//printf("%s", arr);//第一行的字符数不够 读完字符 长度允许再加\n, 末尾再补\0 //关闭文件fclose(pf);pf = NULL;return 0;
}
fprintf
把格式化的数据打印在指定输出流上
会使用 printf就会使用 fprintf,我们来比对这两个函数
发现 fprintf就比 printf多了一个参数 stream:文件流
int printf ( const char * format, ... );
int fprintf ( FILE * stream, const char * format, ... );
参数类型
比printf多了一个参数 stream:流
使用方法
和printf的使用方法一样
struct S
{char name[20];int age;float score;
};
int main()
{struct S s = { "张三",20,65.5f };//想把s中的数据存放在文件中FILE* pf = fopen("test.txt", "w");if (pf == NULL){perror("fopen");return;}//写文件fprintf(pf,"%s %d %f", s.name, s.age, s.score);fclose(pf);pf = NULL;return 0;
}
fscanf
在指定输入流上读取格式化数据
int scanf ( const char * format, ... );
int fscanf ( FILE * stream, const char * format, ... );
参数类型
会使用scanf就会使用fscanf,我们来比对这两个函数
发现fscanf就比scanf多了一个参数 stream:文件流
返回值
成功读取:返回成功填充的参数列表的项数
否则,返回EOF
使用方法
fscanf和scanf的使用方法一样
struct S
{char name[20];int age;float score;
};
int main()
{struct S s = { 0 };//想从文件test.txt中读取数据放到s中FILE* pf = fopen("test.txt", "r");if (pf == NULL){perror("fopen");return;}//读文件fscanf(pf, "%s %d %f", s.name, &s.age, &s.score);//printf("%s %d %f", s.name, s.age, s.score);fprintf(stdout,"%s %d %f", s.name, s.age, s.score);fclose(pf);pf = NULL;return 0;
}
应用
把data.txt 文件的数据拷贝到data_copy.txt文件上
int main()
{FILE* pr = fopen("data.txt", "r");if (pr == NULL){perror("fopen");return;}FILE* pw = fopen("data_copy.txt", "w");if (pw == NULL){perror("fopen");fclose(pw);return;}//读/写文件int ch = 0;while ((ch=fgetc(pr)) != EOF){fputc(ch, pw);}//char ch[20] = { 0 };//while (fgets(ch, 20, pr) != NULL)//{// fputs(ch, pw);//}/*char ch[200] = { 0 };while (fscanf(pr, "%s", ch) != EOF){fprintf(pw, "%s ", ch);}*///关闭文件fclose(pw);pw = NULL;fclose(pr);pr = NULL;return 0;
}
fwrite
以二进制的形式写到流上
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
参数类型
ptr:指向要写入的元素数组的指针
size:要写入的每个元素的大小
count:元素个数
stream:流
使用方法
fwrite 以二进制的形式写进去 从数组里写count个size大小的数据到文件里
int main()
{int arr[] = { 1,2,3,4,5 };FILE* pf = fopen("test.txt", "wb");if (pf == NULL){perror("fopen");return;}//写数据int sz = sizeof(arr) / sizeof(arr[0]);fwrite(arr, sizeof(arr[0]), sz, pf);//以二进制的形式写进去的fclose(pf);pf = NULL;return 0;
}
fread
从流上以二进制的形式读出来
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
参数类型
ptr:指向大小至少为 (size*count) 字节的内存块的指针
size:要写入的每个元素的大小
count:元素个数
stream:流
使用方法
fread 以二进制的形式读出来 从文件里读count个size大小的数据到数组里
int main()
{int arr[5] = { 0 };FILE* pf = fopen("test.txt", "rb");if (pf == NULL){perror("fopen");return;}//读数据int i = 0;while (fread(&arr[i], sizeof(int), 1, pf))//fread 返回值:成功读取到的总元素个数{i++;printf("%d ", arr[i]);}//fread(arr, sizeof(arr[0]), 5, pf);//for (int i = 0; i < 5; i++)//{// printf("%d ", arr[i]);//1 2 3 4 5//}fclose(pf);pf = NULL;return 0;
}
文件读取结束的判定
使用 feof 和 ferror来判断文件读取结束的原因
被错误使用的 feof
牢记:在文件读取过程中,不能用feof函数的返回值直接来判断文件的是否结束。
feof 的作用是:当文件读取结束的时候,判断读取结束的原因是否是:遇到文件尾结束。
- ⽂本⽂件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )
例如:
• fgetc 判断是否为 EOF .
• fgets 判断返回值是否为 NULL . - ⼆进制⽂件的读取结束判断,判断返回值是否⼩于实际要读的个数。
使用方法
⽂件读取结束的判定
int main()
{FILE* pf = fopen("test.txt", "r");if (pf == NULL){perror("fopen");return;}//读取int ch = 0;while ((ch = fgetc(pf)) != EOF){printf("%c\n", ch);}//判断是什么原因导致⽂件读取结束if (feof(pf))//feof 遇到⽂件尾结束 返回一个非零值,否则 返回0{printf("遇到文件末尾,读取正常结束\n");}else if (ferror(pf))//ferror 遇到错误结束 返回非零值,否则 返回0{perror("fgetc");}fclose(pf);pf = NULL;return 0;
}
总结
这里只介绍了顺序读写函数,还有随机读写函数 fseek,ftell,rwind,感兴趣的小伙伴可以自己了解。这次的分享就到这,如果对你有帮助,请给博主点点关注,如果你有任何疑问或想要深入探讨的话题,也欢迎在评论区留言,我们一起来探讨。
