文件操作函数
- 1.文件的打开和关闭
- 1.1fopen
- 1.2close
- 2.文件的顺序读写
- 2.1fgetc和fputc
- 2.1.1fputc
- 2.1.2fgetc
- 2.2fgets和fputs
- 2.2.1fputs
- 2.2.2fgets
- 2.3fscanf和fprintf
- 2.3.1fprintf
- 2.3.2fscanf
- 2.4fread和fwrite
- 2.4.1fwrite
- 2.4.2fread
- 2.5总结
- 2.6拓展(sscanf和sprintf)
- 3.文件的随机读写
- 3.1fseek
- 3.2ftell
- 3.3rewind
- 4.文件读取结束的判定
- 4.1文件是否结束
- 4.2feof和ferror
1.文件的打开和关闭
1.1fopen
FILE * fopen ( const char * filename, const char * mode );
- fopen的作用是打开文件。
- fopen的头文件是<stdio.h>,第一个参数是文件名,第二个参数是方式(以什么方式打开文件),打开文件成功,返回指向文件信息区的指针,打开失败返回NULL。
- 文件的打开方式如下:
文件使用方式 | 含义 | 如果指定文件不存在 |
---|---|---|
“r”(只读) | 为了输入数据,打开一个已经存在的文本文件 | 出错 |
“w”(只写) | 为了输出数据,打开一个文本文件 | 建立一个新的文件 |
“a”(追加) | 向文本文件尾添加数据 | 建立一个新的文件 |
“rb”(只读) | 为了输入数据,打开一个二进制文件 | 出错 |
“wb”(只写) | 为了输出数据,打开一个二进制文件 | 建立一个新的文件 |
“ab”(追加) | 向一个二进制文件尾添加数据 | 出错 |
“r+”(读写) | 为了读和写,打开一个文本文件 | 出错 |
“w+”(读写) | 为了读和写,建立一个新的文件 | 建立一个新的文件 |
“a+”(读写) | 打开一个文件,在文件尾进行读写 | 建立一个新的文件 |
“rb+”(读写) | 为了读和写打开一个二进制文件 | 出错 |
“wb+”(读写) | 为了读和写,新建一个新的二进制文件 | 建立一个新的文件 |
“ab+”(读写) | 打开一个二进制文件,在文件尾进行读和写 | 建立一个新的文件 |
(1)这里的输入和输出是站在内存的角度上的,输入是从文件输入到内存,输出是从内存输出到文件。
(2)什么是文本文件?什么又是二进制文件?
数据在内存中以二进制的形式存储,如果不加转换的输出到外存(文件),就是二进制文件。如果要求在外存(文件)上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件。
(3)不要看这么多打开方式,在C常用的只有"r"、“w”、"rb"和"wb"这4种。
1.2close
int fclose ( FILE * stream );
- fclose的作用是关闭文件。
- (1)什么是流(stream)?我认为是数据输入输出的一种介质。将数据输出到屏幕、输出到文件,从文件、键盘输入数据都需要流(stream)的帮助。
(2)为什么我们平时打印数据、输入数据都不需要流?任何一个C语言程序运行的时候,默认打开三个流:stdin:标准输入流(键盘);stdout:标准输出流(屏幕);stderr:标准错误流(屏幕)。
(3)所以我们在使用文件输入输出时需要流。我们从文件读写数据,都需要经过流,所以直接从流读写即可。流就是我们的文件指针,类型是FILE*,通过这个流我们可以输入数据到内存,也可以输出数据到文件。 - 文件成功关闭就返回0,失败就返回EOF。同时记得把指针置为NULL。
例子
#include<stdio.h>
int main()
{
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fclose(pf);
pf = NULL;
return 0;
}
结果
2.文件的顺序读写
2.1fgetc和fputc
2.1.1fputc
int fputc ( int character, FILE * stream );
- fputc的作用是输出字符到流中。fputc适用于所有输出流(包括stdout和文件等)。
- fputc的头文件是<stdio.h>。第一个参数是要输入的字符,第二个参数是指向输出流的指针。如果输出成功,返回输出的字符,如果失败返回EOF。
例子
首先创建一个文件。
int main()
{
FILE* pf = fopen("test.txt", "w");//我们要写入文件,以写的形式打开
if (NULL == pf)
{
perror("fopen");
return 1;
}
int i = 0;
for (i = 'a'; i <= 'z'; i++)
{
fputc(i, pf);//文件里面有指示器,当输入一个字符时,指示器指示下一个位置。
}
fclose(pf);
pf = NULL;
return 0;
}
结果
2.1.2fgetc
int fgetc ( FILE * stream );
- fgetc的作用是从流中读取字符。fgetc适用于所有输入流(包括stdin和文件等)。
- fgetc的头文件是<stdio.h>。参数是指向输入流的指针。如果读取成功,返回读取字符的ASCII码值,如果读取结束或者读取失败就返回EOF。
例子
int main()
{
FILE* pf = fopen("test.txt", "r");//以读的形式打开文件
if (NULL == pf)
{
perror("fopen");
return 1;
}
//读取文件
int ch = 0;
while (ch = fgetc(pf))//当读取一个字符后,指示器指向下一个字符
{
printf("%c ", ch);
}
fclose(pf);
pf = NULL;
return 0;
}
结果
2.2fgets和fputs
2.2.1fputs
int fputs (const char * str, FILE * stream);
- fputs的作用是将字符串写入流中。fputs适用于所有输出流。
- fputs的头文件是<stdio.h>。第一个参数是指向要写入流的字符串的指针,第二个参数是指向输出流的指针。如果写入成功就返回一个非负数,失败就返回EOF。
- fputs将字符串写入流,遇到’\0’停止,但末尾的’\0’并不会被写入。
例子
int main()
{
FILE* pf = fopen("test.txt", "w");//以"w"的形式打开,如果先前文件有内容就会被初始化
if (NULL == pf)
{
perror("fopen");
}
fputs("hello world\n", pf);
fputs("welcome", pf);
fclose(pf);
pf = NULL;
return 0;
}
结果
2.2.2fgets
char * fgets ( char * str, int num, FILE * stream );
- fgets的作用是从流中读取字符串。fgets适用于所有输入流。
- fgets的头文件是<stdio.h>。第一次参数是指向一个数组的指针,这个数组用来放从流中读取的字符串,第二个参数是从流中读取的字符个数(实际上是读取num-1个字符,最后一个字符留给\0),第三个参数是指向输入流的指针。如果读取成功则返回str,读取到文件结束或者读取失败就返回NULL。(读取失败和读取到文件结束该如何区分,后面会讲到)
- fgets从流中读取字符串,直到num-1个字符结束,或者读取到换行符结束(换行符也算入num个字符中),或者读取到文件末尾结束。
例子
int main()
{
FILE* pf = fopen("test.txt", "r");
if (NULL == pf)
{
perror("fopen");
return 1;
}
char arr[20] = { 0 };
fgets(arr, 5, pf);
printf("%s", arr);
//fgets(arr, 8, pf);
//printf("%s", arr);
return 0;
}
结果
2.3fscanf和fprintf
2.3.1fprintf
int fprintf ( FILE * stream, const char * format, ... );
- fprintf的作用是将数据格式化输出到流中。fprintf适用于所有输出流。
- fprintf的头文件是<stdio.h>。第一个参数是指向输出流的指针,第二个参数是可变参数列表,即可以有多个参数,没有固定。如果输出成功则返回输出的项目数,如果失败就返回负数。
例子
struct Stu
{
char name[20];
int age;
float score;
};
int main()
{
struct Stu s = { "zhangsan",18,99.5 };
FILE* pf = fopen("test.txt", "w");
if (NULL == pf)
{
perror("fopen");
return 1;
}
fprintf(pf, "%s %d %f", s.name, s.age, s.score);
fclose(pf);
pf = NULL;
return 0;
}
结果
2.3.2fscanf
int fscanf ( FILE * stream, const char * format, ... );
- fscanf的作用是从流中格式化地读取数据放到参数列表中的项目。fscanf适用于所有输入流。
- fscanf的头文件是<stdio.h>。第一个是指向输入流的指针,第二个参数是可变参数列表。如果读取成功就返回该函数返回参数列表中成功填充的项目数,如果读取失败或者到达文件末尾就返回EOF。
例子
struct Stu
{
char name[20];
int age;
float score;
};
int main()
{
struct Stu s = { 0 };
FILE* pf = fopen("test.txt", "r");
if (NULL == pf)
{
perror("fopen");
return 1;
}
fscanf(pf, "%s %d %f", s.name, &s.age, &s.score);
printf("%s %d %f", s.name, s.age, s.score);
return 0;
}
结果
2.4fread和fwrite
2.4.1fwrite
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
- fwrite的作用是将数据块写入流。fwirte只适用于文件。
- fwirte的头文件是<stdio.h>。第一次参数是指向要写入流的数组的指针,第二个参数是数组元素的大小,第三个参数是数组元素的数目,第四个是指向输出流的指针。如果输出成功则返回写入流的元素总数。如果size和count都为0 ,则返回0。
例子
struct Stu
{
char name[20];
int age;
float score;
};
int main()
{
FILE* pf = fopen("test.txt", "wb");//前面的函数读写文件都是文本信息,都可以看懂
//fread和fwrite读写文件都是二进制信息
if (NULL == pf)
{
perror("fopen");
return 1;
}
struct Stu s = { "zhangsan",20,99.5 };
fwrite(&s, sizeof(struct Stu), 1, pf);
fclose(pf);
pf = NULL;
return 0;
}
结果
2.4.2fread
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
- fread的作用是从流中读取数据放到内存块中。fread只适用于文件。
- fread的头文件是<stdio.h>。第一个参数是指向数组的指针,这个数组的大小至少是size*count,这样才能存放从流中读取的数据,第二个参数是数组元素的大小,第三个参数是数组元素的数目,第四个参数是指向输入流的指针。如果读取成功,返回读取元素总数,读取失败或者读取到文件末尾,都设置了各自的指示符,这些指示符可以与feror和feof搭配使用,从而得出是读取是失败还是读取到文件末尾(后面会讲到),如果count或者size是0,则返回0。
例子
struct Stu
{
char name[20];
int age;
float score;
};
int main()
{
FILE* pf = fopen("test.txt", "rb");
if (NULL == pf)
{
perror("fopen");
return 1;
}
struct Stu s = { 0 };
fread(&s, sizeof(struct Stu), 1, pf);
printf("%s %d %f", s.name, s.age, s.score);
fclose(pf);
pf = NULL;
return 0;
}
结果
2.5总结
功能 | 函数名 | 适用于 |
---|---|---|
字符输入函数 | fgetc | 所有输入流 |
字符输出函数 | fputc | 所有输出流 |
文本行输入函数 | fgets | 所有输入流 |
文本行输出函数 | fputs | 所有输出流 |
格式化输入函数 | fscanf | 所有输入流 |
格式化输出函数 | fprintf | 所有输出流 |
二进制输入 | fread | 文件 |
二进制输出 | fwrite | 文件 |
2.6拓展(sscanf和sprintf)
- scanf从键盘读取格式化的数据(输入流:键盘stdin),printf把数据输出到屏幕上(输出流:屏幕stdout)。
- fscanf针对所有输入流的格式化的输入(输入流:stdin,打开的文件),fprintf针对所有输出流的格式化的输出(输出流:stdout、打开的文件)。
- sscanf从一个字符串中还原出一个格式化的数据,sprintf把格式化的数据存放在一个字符串。
例子
struct Stu
{
char name[20];
int age;
float score;
};
int main()
{
struct Stu s = { "zhangsan",18,99.5 };
char buf[100] = { 0 };
sprintf(buf, "%s %d %f", s.name, s.age, s.score);
printf("%s\n", buf);
printf("-------分割线---------\n");
struct Stu tmp = { 0 };
sscanf(buf, "%s %d %f", tmp.name, &tmp.age, &tmp.score);
printf("%s %d %f", tmp.name, tmp.age, tmp.score);
return 0;
}
结果
3.文件的随机读写
3.1fseek
int fseek ( FILE * stream, long int offset, int origin );
- fseek的作用是重新定位流中的位子指示器指向的位置。它是根据文件指针的位置和偏移量来定位文件指针。
- 头文件是<stdio.h>。第一个参数是指向流的指针,第二个参数是偏移量,第三个参数是起始位置,是偏移量的参考位置,它有固定的三个取值:SEEK_SET(文件开头)、SEEK_CUR(文件指针当前位置)、SEEK_END(文件末尾)。如果成功返回零,失败返回非零值。
例子
int main()
{
FILE* pf = fopen("test.txt", "r");//先在文件内写好abcdef,现在用读的形式打开
if (NULL == pf)
{
perror("fopen");
return 1;
}
//文件的顺序读写,读完一个字符后,文件指针指向下一个字符
printf("%c\n", fgetc(pf));//a
printf("%c\n", fgetc(pf));//b
printf("%c\n", fgetc(pf));//c
//按照顺序读写,接下来打印的是d,但我想要打印b
fseek(pf, -2, SEEK_CUR);//当前位置是文件指针(位置指示器)指向d,后前偏移2就指向b
printf("%c\n", fgetc(pf));
//法2: fseek(pf,1,SEEK_SET);从开头开始偏移
// printf("%c\n",fgetc(pf));
//法3: fseek(pf,-4,SEEK_END);//从末尾开始偏移
// printf("%c\n", fgetc(pf));
fclose(pf);
pf = NULL;
return 0;
}
结果
3.2ftell
long int ftell ( FILE * stream );
- ftell的作用是返回文件指针相对于起始位置的偏移量。
- 头文件是<stdio.h>,参数是指向流的指针,返回值是当前文件指针相对于起始位置的偏移量。
例子
int main()
{
FILE* pf = fopen("test.txt", "r");
if (NULL == pf)
{
perror("fopen");
return 1;
}
printf("%c\n", fgetc(pf));//a
printf("%c\n", fgetc(pf));//b
printf("%d\n", ftell(pf));
return 0;
}
结果
3.3rewind
void rewind ( FILE * stream );
- rewind的作用是让文件指针的位置回到文件的起始位置。
- 头文件是<stdio.h>,参数是指向流的指针,无返回值。
例子
int main()
{
FILE* pf = fopen("test.txt", "r");
if (NULL == pf)
{
perror("fopen");
return 1;
}
printf("%c\n", fgetc(pf));//a
printf("%c\n", fgetc(pf));//b
printf("%d\n", ftell(pf));//2
rewind(pf);
printf("%c\n", fgetc(pf));//a
return 0;
}
结果
4.文件读取结束的判定
4.1文件是否结束
- 文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )
例如:
fgetc 判断是否为 EOF
fgets 判断返回值是否为 NULL - 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。
例如:
fread判断返回值是否小于实际要读的个数
4.2feof和ferror
- 在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束。
而是应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束。 - feof返回真,说明是文件正常读取遇到了结束标志而结束的。
- ferror返回真,说明文件在读取过程中出错了,结束的。
例子
int main(void)
{
int c; // 注意:int,非char,要求处理EOF
FILE* fp = fopen("test.txt", "r");
if(!fp) {
perror("File opening failed");
return EXIT_FAILURE;
}
//fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF
while ((c = fgetc(fp)) != EOF) // 标准C I/O读取文件循环
{
putchar(c);
}
//判断是什么原因结束的
if (ferror(fp))
puts("I/O error when reading");
else if (feof(fp))
puts("End of file reached successfully");
fclose(fp);
}