文件操作
C语言输入输出的对象,主要就是标准输出流(stdout)与标准输入流(stdin)。所谓标准输出流通俗地说就是屏幕输出,标准输入流就是键盘输入。除了这两种之外,C语言还可以对磁盘文件和外部设备输入输出,这就用到了文件读写函数。
1、文件的打开关闭
FILE是系统使用typedef定义出来的有关文件信息的一种结构体类型,结构中含有文件名、文件状态和文件当前位置等信息。
声明FILE结构体类型的信息包含在头文件“stdio.h”中,一般设置一个指向FILE类型变量的指针变量,然后通过它来引用这些FILE类型变量。通过文件指针就可对它所指的文件进行各种操作。
fopen函数
1、功能:打开文件
2、函数格式:
#include <stdio.h>
FILE* fopen(const char* filename, const char* mode);
3、函数参数:
filename:需要打开的文件名,根据需要加上路径
mode:打开文件的模式设置
4、函数返回值:
成功:文件指针
失败:NULL
fclose函数
1、功能:关闭先前fopen()打开的文件。此动作让缓冲区的数据写入文件中,并释放系统所提供的文件资源。
2、函数格式:
#include <stdio.h>
int fclose(FILE* stream);
3、函数参数:
stream:文件指针
4、函数返回值:
成功:0
失败:-1
5、fclose函数注意点:
-
打开的文件会占用内存资源,如果总是打开不关闭,会消耗很多内存
-
一个进程同时打开的文件数是有限制的,超过最大同时打开文件数,再次调用fopen打开文件会失败
-
如果没有明确的调用fclose关闭打开的文件,那么程序在退出的时候,操作系统会统一关闭。
2、文件的打开与关闭代码实现
#define _CRT_SECURE_NO_WANINGS
#include <stdio.h>
// 1. 文件打开关闭
void test01(){
// r 以只读方式打开一个文本文件(不创建文件,若文件不存在则报错)
// 下面写法不推荐
// fopen("C:\\Users\\cpp\\Documents\\codes\\day12\\day12\\demo1.txt", "r");
// 大部分系统都支持下面的路径写法(绝对路径)
// fopen("C:/Users/cpp/Documents/codes/day12/day12/demo1.txt", "r");
// 相对路径
FILE* fp = fopen("./demo1.txt", "r");
// 返回的 FILE 指针,指向了一块内存,这块内存就存储了当前打开文件的信息
// FILE 指针指向的空间是由系统帮我们申请的
// 文件打开失败,返回 NULL
if (NULL == fp){
printf("文件打开失败!\n");
return;
}
// 关闭文件
// 如果打开文件没有关闭,当程序结束的时候,文件也被关闭。
// 文件使用完毕,要及时释放。
// 1. fp 指向的内存就会被释放.
// 2. 刷新缓冲区,会将缓冲区中的文件内容写入到磁盘中
// 3. 对于程序,可打开的文件数量有上限的。
fclose(fp);
fp = NULL;
}
int main(){
test01();
return 0;
}
3、文件打开模式
打开模式 | 欲操作的文件不存在 | 成功打开之后的指针位置 | 是否清空原先内容 |
---|---|---|---|
r | 打开失败 | 开头 | 否 |
w | 新建 | 开头 | 是 |
a | 新建 | 结尾 | 否 |
r+ | 打开失败 | 开头 | 否 |
w+ | 新建 | 开头 | 是 |
a+ | 新建 | 结尾 | 否 |
4、文件字符读写
-
单个字符输入输出函数:fgetchar与fputchar(或者getc与putc);
-
单行字符串输入输出函数:fgets与fputs;
-
格式化输入输出函数:fscanf与fprintf;
Ⅰ、文件字符 写 fputc 函数
1.函数格式: #include <stdio.h>
int fputc(int ch, FILE* stream);
2.函数功能: 将ch转换为unsigned char后写入stream指定的文件中
3.函数参数: ch:需要写入文件的字符
stream:文件指针
4.函数返回值:成功:成功写入文件的字符
失败:返回-1
Ⅱ、文件字符 读 fgetc函数
1.函数格式:#include <stdio.h>
int fgetc(FILE* stream);
2.函数功能:从stream指定的文件中读取一个字符
3.函数参数:stream:文件指针
4.函数返回值:成功:返回读取到的字符
失败:-1
5、测试文件的写操作
#define _CRT_SECURE_NO_WANINGS
#pragma warning(disable : 4996)
#include <stdio.h>
#include <stdio.h>
int main(){
char s[80]="\0";
FILE* pf = fopen("./小柯南.txt", "w");
puts("请输入一行文字:"); //fputs("请输入一行文字:", stdout);
scanf_s("%s", s,sizeof(s)); //fgets(s, sizeof(s), stdin);
fputs(s, pf);
fclose(pf);
return 0;
}
6、测试文件的读操作
#define _CRT_SECURE_NO_WANINGS
#pragma warning(disable : 4996)
int main(){
int c;
FILE* pf = fopen("./小柯南.txt", "r");
if (!pf)
{
puts("打开文件失败!");
return -1;
}
while (1){
c = getc(pf);
if (c == -1)
break;
if (c == '\r')
continue;
putchar(c); //putc(c,stdout);
}
fclose(pf);
return 0;
}
//linux文本文件的换行符是:\n
//windows文本文件的换行符是:\r\n
7、判断文件结束(EOF、feof函数)、实现文件拷贝
Ⅰ、EOF函数
在C语言中,EOF表示文件结束符(end of file)。在while循环中以EOF作为文件结束标志,这种以EOF作为文件结束标志的文件,必须是文本文件。在文本文件中,数据都是以字符的ASCII代码值的形式存放。我们知道,ASCII代码值的范围是0~127,不可能出现-1,因此可以用EOF作为文件结束标志。
函数格式:
#define EOF (-1)
Ⅱ、feof 函数
当把数据以二进制形式存放到文件中时,就会有-1值的出现,因此不能采用EOF作为二进制文件的结束标志。为解决这一个问题,ANSI C提供一个feof函数,用来判断文件是否结束。feof函数既可用以判断二进制文件又可用以判断文本文件。
函数格式:
#include <stdio.h>
int feof(FILE* stream);
函数功能:
检测是否读取到了文件结尾。判断的是最后一次“读操作的内容”,不是当前位置内容。
函数参数:
- stream:文件指针
函数返回值:
-
非0值:已经到文件结尾
-
0:没有到文件结尾
8、判断文件结束
#define _CRT_SECURE_NO_WANINGS
#pragma warning(disable : 4996)
#include <stdio.h>
// 1. 文件打开关闭
void test01() {
//1.打开文件
FILE* fp = fopen("./demo.txt", "r");
if (NULL == fp) {
printf("文件打开失败!\n");
return;
}
// 2. 读取文件内容
char ch = 0;
// "abc[-1]"
while (1) {
ch = fgetc(fp);
// 如果 feof 函数返回 true, 表示文件结束
if (feof(fp))
{
break;
}
printf("%c", ch);
}
// 文件关闭
fclose(fp);
fp = NULL;
}
int main() {
test01();
return 0;
}
9、实现文件拷贝
#define _CRT_SECURE_NO_WANINGS
#pragma warning(disable : 4996)
#pragma warning(disable : 6031)
#include <stdio.h>
#include <stdio.h>
/*实现思路:
1. 输入待拷贝的文件路径.
2. 输入拷贝的目的地路径.
3. 打开两个文件 :
1. 待拷贝文件 : 以读的方式打开
2. 目的地文件 : 以写的方式打开
4. 文件拷贝
1. 从待拷贝文件中读取一个字符
2. 将该字符写入到目的地文件
5. 关闭两个文件。
```c
*/
void test(){
// 1. 获得文件路径
char file_src[128] = { 0 };
char file_dst[128] = { 0 };
printf("请输入待拷贝文件路径:");
scanf("%s", file_src);
//fflush(file_src);
printf("请输入目的地文件路径:");
scanf("%s", file_dst);
// 2. 打开文件
FILE* fp_read = fopen(file_src, "r");
if (NULL == fp_read){
printf("文件: %s 打开失败!\n", file_src);
return;
}
FILE* fp_write = fopen(file_dst, "w");
if (NULL == fp_write){
// 关闭前面打开的文件
fclose(fp_read);
fp_read = NULL;
printf("文件: %s 打开失败!\n", file_dst);
return;
}
// 3. 拷贝实现
char ch = 0;
// 从待拷贝文件中读取内容
while ((ch = fgetc(fp_read)) != EOF)
{
// 将读取的内容写入到目的地文件中
fputc(ch, fp_write);
}
// 4. 关闭文件
fclose(fp_read);
fp_read = NULL;
fclose(fp_write);
fp_write = NULL;
printf("%s 拷贝到 %s 成功!\n", file_src, file_dst);
}
int main() {
test();
return 0;
}
10、行文件读写
Ⅰ、fputs函数
函数格式:
#include <stdio.h>
int fputs(const char* str, FILE* stream);
函数功能:
将str所指定的字符串写入到stream指定的文件中,字符串结束符 ‘\0’ 不写入文件。
函数参数:
- str:字符串
- stream:文件指针
函数返回值:
-
成功:0
-
失败:-1
Ⅱ、fgets函数
函数格式:
#include <stdio.h>
char* fgets(char* str, int size, FILE* stream);
函数功能:
从stream指定的文件内读入字符,保存到str所指定的内存空间,直到出现换行字符、读到文件结尾或是已读了size - 1个字符为止,最后会自动加上字符 ‘\0’ 作为字符串结束。
函数参数:
-
str:字符串
-
size:指定最大读取字符串的长度(size - 1)
-
stream:文件指针
函数返回值:
-
成功:成功读取的字符串
-
读到文件尾或出错: NULL
11、行文件写读案例
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
// 1. 把内容写入到文件中
void test01(){
// 1. 打开文件
FILE* fp = fopen("./小柯南.txt", "w");
if (NULL == fp)
{
return;
}
// 2. 写文件
#if 0
char* s = "hello world";
// 从 s 字符串第一个字符开始到 \0 之前的内容写入到文件中
fputs(s, fp);
#endif
// 字符串指针数组
char* name[] = { "conan\n", "sim\n", "deral\n", "Browers\n", "cliner\n", "pitcher\n" };
for (int i = 0; i < sizeof(name) / sizeof(name[0]); ++i)
{
fputs(name[i], fp);
}
// 3. 关闭文件
fclose(fp);
fp = NULL;
}
// 2. 从文件中读取出来
void test02(){
// 1. 打开文件
FILE* fp = fopen("./小柯南.txt", "r");
if (NULL == fp)
{
return;
}
// 2. 读文件
while (1)
{
// 申请空间,用于存储文件内容
char line[128] = { 0 };
// 从文件中一次读取一行
fgets(line, 128, fp);
// 判断文件是否结束
if (feof(fp))
{
break;
}
// 打印文件内容
printf("%s", line);
}
// 3. 关闭文件
fclose(fp);
fp = NULL;
}
int main() {
test01();
test02();
return 0;
}
12、文件输入输出函数等价关系
- 1.单个字符输入输出函数:
getchar()与fgetchar(stdin)以及getc(stdin)三者是等价的;
putchar(c)与fputchar(c, stdout)以及putc(c,stdout)三者是等价的;
- 2.单行字符串输入输出函数:
gets(s)与fgets(s,sizeof(s),stdin)两者是等价的;
puts(s)与fputs(s,stdout)两者是等价的;
- 3.格式化输入输出函数:
scanf(x,…)与fscanf(stdin,x,…)是等价的;
printf(x,…)与fprintf(stdout,x,…)是等价的;
13、getchar失效问题
getchar失效问题,有两种解决办法:
1.在调用单字符输入函数getchar或者scanf("%c "…)之前,执行fflush函数清理一下输入缓冲区;
2.使用scanf("\n%c"…)代替getchar函数,可以很好地解决单字符输入失效的问题。
14、清空缓冲区fflush()函数
Ⅰ、fflush()函数
此函数包含在stdio.h头文件中,用来强制将缓冲区中的内容写入文件。
1.函数原型:
int fflush(FILE *stream) ;
2.函数功能:
清除一个流,即清除文件缓冲区.
3.适用场合:
一、ff(stdin)与fflush(stdout)的区别
ff(stdin)刷新标准输入缓冲区,把输入缓冲区里的东西丢弃
fflush(stdout)刷新标准输出缓冲区,把输出缓冲区里的东西打印到标准输出设备上。
二、scanf()函数接收输入数据时,结束一个数据的输入的情况
scanf()函数接收输入数据时,遇以下情况结束一个数据的输入:(不是结束该scanf函数, scanf函数仅在每一个数据域均有数据,并按回车后结束)。
① 遇空格、“回车”、“跳格”键。
② 遇宽度结束。
③ 遇非法输入。
遇到上面的情况,键盘缓冲区就可能有残余信息问题。
scanf()函数应该只是扫描stdin流,这个残存信息是在stdin中,要解决这个问题就要在scanf()函数之后加个fflush(stdin)。
三、文件与缓存
写文件的时候 不会马上进行物理磁盘的文件读写,而是先写入缓存,当缓存中内容达到一定程度后再写,例如:
FILE *fp=fopen("小柯南.txt","w");
fprintf(fp,"xiaokenan");
fflush(fp);