1、文件指针
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字、状态、位置等)。这些信息是保存在一个结构体变量中。该结构体类型是有系统声明的,取名FILE.
2、打开文件fopen和关闭文件fclose
FILE * fopen ( const char * filename, const char * mode );
filename为文件命,如果打开的文件在工程目录下需要表明文件的绝对位置,mode为文件的方式。
文件使用方式 | 含义 | 如果指定文件不存在 |
"r"(只读) | 为了输入数据,打开一个已经存在的文文件 | 出错 |
"w"(只写) | 为了输出数据,打开一个文本文件,如果文件存在,会将原文件的内容销毁 | 建立一个新的文件 |
"a"(追加) | 向文本文件尾添加数据 | 建立一个新的文件 |
"rb"(只读) | 为了输入数据,打开一个二进制文件 | 出错 |
"wb"(只写) | 为了输出数据,打开一个二进制文件 | 建立一个新的文件 |
"ab"(追加) | 向一个二进制文尾添加数据 | 出错 |
"r+"(读写) | 为了读和写,打开一个文本文件 | 出错 |
"w+"(读写) | 为了读和写,建立一个新的文件 | 新建一个新的文件 |
"a+"(读写) | 打开一个文件,在文件尾进行读写 | 建立一个新的文件 |
"rb+"(读写) | 为了读和写打开一个二进制文件 | 出错 |
"wb+"(读写) | 为了读和写,新建一个新的二进制文件 | 建立一个新的文件 |
"ab+"(读写) | 打开一个二进制文件,在文件尾进行读和写 | 建立一个新的文件 |
int fclose ( FILE * stream );
fclose:关闭文件的函数
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE* pf = fopen("test.txt", "w");
if (NULL == pf)
{
printf("open file for writing :%s", strerror(errno));
return 0;
}
//写文件
//关闭
fclose(pf);
pf = NULL;
return 0;
}
3、文件的顺序读写
1、字符输出函数fputc
int fputc ( int character, FILE * stream );
将一个字符写入到输出流,如果成功,返回写入字符的ASCAII码值,如果失败返回EOF
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE* pf = fopen("test.txt", "w");
if (NULL == pf)
{
printf("open file for writing :%s", strerror(errno));
return 0;
}
//写文件
for (int i = 'a'; i <= 'z'; i++)
{
fputc(i, pf);
}
//关闭
fclose(pf);
pf = NULL;
return 0;
}
2、字符输入函数fgetc
int fgetc ( FILE * stream );
从流里得到一个字符,成功返回读到的字符,如果文件结果或者读取错误返回EOF
int main()
{
FILE* pf = fopen("test.txt", "r");
if (NULL == pf)
{
printf("open file for writing :%s", strerror(errno));
return 0;
}
int ch = 0;
//读文件
ch = fgetc(pf);
printf("%c\n",ch);
ch = fgetc(pf);
printf("%c\n",ch);
ch = fgetc(pf);
printf("%c\n",ch);
//关闭
fclose(pf);
pf = NULL;
return 0;
}
3、文本输出函数fputs
int fputs ( const char * str, FILE * stream );
写入一个字符串,成功返回一个非负值,失败返回EOF
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE* pf = fopen("test.txt", "w");
if (NULL == pf)
{
printf("open file for writing :%s", strerror(errno));
return 0;
}
//写文件
fputs("abcdef", pf);
//关闭
fclose(pf);
pf = NULL;
return 0;
}
4、文本输入函数fgets
char * fgets ( char * str, int num, FILE * stream );
从sream中读取字符串到一个字符数组,num为最大的读取数(实际读的个数为num-1个),成功返回这个字符数组,读取失败或者读取结束返回NULL
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE* pf = fopen("test.txt", "r");
if (NULL == pf)
{
printf("open file for writing :%s", strerror(errno));
return 0;
}
//读文件
char buf[100] = {0};
fgets(buf, 3, pf);
printf("%s\n", buf);
//关闭
fclose(pf);
pf = NULL;
return 0;
}
5、格式化输出函数fprintf
int fprintf ( FILE * stream, const char * format, ... );
将格式化的数据写到stream
#include <stdio.h>
#include <string.h>
#include <errno.h>
typedef struct Stu
{
char name[5];
int age;
char sex[5];
}Stu;
int main()
{
Stu s1 = { "lisi",20,"male" };
FILE* pf = fopen("test.txt", "w");
if (NULL == pf)
{
printf("open file for writing:%s", strerror(errno));
return 0;
}
fprintf(pf, "%s %d %s", s1.name,s1.age, s1.sex);
//关闭
fclose(pf);
pf = NULL;
return 0;
}
6、格式化输入函数fscanf
int fscanf ( FILE * stream, const char * format, ... );
将格式化的数据读入stream
#include <stdio.h>
#include <string.h>
#include <errno.h>
typedef struct Stu
{
char name[5];
int age;
char sex[5];
}Stu;
int main()
{
Stu s1 = { "lisi",20,"male" };
Stu s2 = { 0 };
FILE* pf = fopen("test.txt", "r");
if (NULL == pf)
{
printf("open file for reading:%s", strerror(errno));
return 0;
}
fscanf(pf, "%s %d %s", s2.name, &s2.age, s2.sex);
printf("%s %d %s", s2.name, s2.age, s2.sex);
//关闭
fclose(pf);
pf = NULL;
return 0;
}
7、二进制输出fwrite
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
从ptr开始,写入count*size大小的数据到stream
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <errno.h>
typedef struct Stu
{
char name[10];
int age;
char sex[10];
}Stu;
int main()
{
Stu s[2] = { {"lisi",20,"male"},{"zhangsan",18,"female"}};
FILE* pf = fopen("test.txt", "wb");
if (NULL == pf)
{
printf("open file for writing:%s", strerror(errno));
return 0;
}
//写文件
fwrite(s, sizeof(Stu), 2, pf);
//关闭
fclose(pf);
pf = NULL;
return 0;
}
8、二进制输出fread
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
从stream中读取大小为size*count的数据,返回成功读取到的元素个数
#include <stdio.h>
#include <string.h>
#include <errno.h>
typedef struct Stu
{
char name[10];
int age;
char sex[10];
}Stu;
int main()
{
Stu s[2] = {0};
FILE* pf = fopen("test.txt", "rb");
if (NULL == pf)
{
printf("open file for reading:%s", strerror(errno));
return 0;
}
//读文件
fread(s, sizeof(Stu), 2, pf);
for (int i = 0; i < 2; i++)
{
printf("%s %d %s \n", s[i].name, s[i].age,s[i].sex);
}
//关闭
fclose(pf);
pf = NULL;
return 0;
}
需要注意fread函数返回的是读取的元素个数,所以当返回值小于count的时候说明数据已经全部读完
4、对比scanf/fscanf/sscanf/printf/fprintf/sprintf
scanf从标准输入流(stdin)上进行格式化输入的函数
printf向标准输出流(stdout)上进行格式化的输出函数
fsacnf可以从标准输入流(stdin)或者指定的文件流上读取格式化的数据
fprintf把数据按照格式的方式输出到标准输出流(stdout)或者指定的文件流
sscanf将字符串转化成格式化的数据
int sscanf ( const char * s, const char * format, ...);
sprintf将格式化的数据转化成字符串
int sprintf ( char * str, const char * format, ... );
#include <stdio.h>
#include <string.h>
#include <errno.h>
typedef struct Stu
{
char name[10];
int age;
char sex[10];
}Stu;
int main()
{
Stu s= {"zhangsan",12,"male"};
char buf[100];
sprintf(buf,"%s %d %s", s.name, s.age, s.sex);
printf("%s", buf);
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <errno.h>
typedef struct Stu
{
char name[10];
int age;
char sex[10];
}Stu;
int main()
{
Stu s1= {"zhangsan",12,"male"};
Stu s2 = { 0 };
char buf[100];
sprintf(buf,"%s %d %s", s1.name, s1.age, s1.sex);
sscanf(buf, "%s %d %s", s2.name, &s2.age, s2.sex);
printf("%s %d %s", s2.name,s2.age,s2.sex);
return 0;
}
5、文件的随机读写
1、fseek
int fseek ( FILE * stream, long int offset, int origin );
根据文件指针的位置和偏移量来定位文件指针
offset为偏移量,origin为起始位置
origin有三个状态
1.SEEK_SET 文件的开始位置
2.SEEK_CUR当前文件指针所指的位置
3.SEEK_END文件结束的位置
比如:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE* pw = fopen("test.txt", "w");
if (NULL == pw)
{
printf("open file for writing: %s", strerror(errno));
return 0;
}
fputs("abcdef", pw);
fflush(pw);//fputs会把数据先写入缓存区,而不是直接进文件,所以需要刷新缓冲区,让文件写入,方便后面的读
FILE* pr = fopen("test.txt", "r");
if (NULL == pr)
{
printf("open file for reading:%s", strerror(errno));
return 0;
}
//直接读到f
fseek(pr, -1, SEEK_END);
int ch = 0;
ch = fgetc(pr);
printf("%c", ch);
fclose(pr);
pr = NULL;
//将x写到c的位置
fseek(pw, 2, SEEK_SET);
fputc('x', pw);
fclose(pw);
pw = NULL;
return 0;
}
2、ftell
long int ftell ( FILE * stream );
返回文件指针相对于起始位置的偏移量
int main()
{
FILE* pf = fopen("test.txt", "r");
if (NULL == pf)
{
printf("open file for writing :%s", strerror(errno));
return 0;
}
int ch = 0;
//读文件
ch = fgetc(pf);
printf("%c\n",ch);
ch = fgetc(pf);
printf("%c\n",ch);
ch = fgetc(pf);
printf("%c\n",ch);
//进行3次读取后,文件指针与起始位置的偏移量应该为3
printf("%d\n", ftell(pf));
//关闭
fclose(pf);
pf = NULL;
return 0;
}
3、rewind
void rewind ( FILE * stream );
让文件指针的位置回到文件的起始位置
int main()
{
FILE* pf = fopen("test.txt", "r");
if (NULL == pf)
{
printf("open file for writing :%s", strerror(errno));
return 0;
}
int ch = 0;
//读文件
ch = fgetc(pf);
printf("%c\n",ch);
ch = fgetc(pf);
printf("%c\n",ch);
ch = fgetc(pf);
printf("%c\n",ch);
//进行3次读取后,文件指针与起始位置的偏移量应该为3
printf("%d\n", ftell(pf));
//将文件指针的位置回到文件的起始位置
rewind(pf);
printf("%d\n", ftell(pf));
//关闭
fclose(pf);
pf = NULL;
return 0;
}
6、文件拷贝
知道文件的输入输出后,我们就可以写一个文件拷贝的代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE* pr = fopen("data1.txt", "r");
FILE* pw = fopen("data2.txt", "w");
if (NULL == pr)
{
printf("open file for reading :%s", strerror(errno));
return 0;
}
if (NULL == pw)
{
printf("open file for writing :%s", strerror(errno));
//读数据的文件指针已经打开,需要关闭
fclose(pr);
pr = NULL;
return 0;
}
char buf[1000] = { 0 };
while (fgets(buf, 1000, pr))//读文件
{
//写文件
fputs(buf, pw);
}
//关闭
fclose(pw);
fclose(pr);
pw = NULL;
pr = NULL;
return 0;
}
7、二进制文件与文本文件
数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件
如果要求在外存上以ASCII码的形式存储,则需要在存储前转换,以ASCII字符的形式存储的文件就是文本文件
8、文件读取结束的标志
1、被错误使用的feof
feof函数的返回值直接用来判断文件是否结束,而是应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束
文本文件读取是否结束
fgetc判断是否为EOF
fgets判断是否为NULL
二进制文件的读取结束判断
fread的返回值是否小于实际要读的个数
#include <stdio.h>
#include <stdlib.h>
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);
}