目录
- 为什么使用文件
- 什么是文件
- 二进制文件和文本文件?
- 文件的打开和关闭
- 文件的顺序读写
- 文件的随机读写
- 文件读取结束的判定
- 文件缓冲区
1、为什么使用文件
如果没有文件,我们写的程序是不能被长久保存的,因为程序的数据存储在电脑的内存中,程序退出,内存就会被回收,数据就会丢失,因此想要将数据长久的保存是少不了文件的
2、什么是文件
磁盘上的文件是文件,但是在程序设计中,我们⼀般谈的⽂件有两种:程序文件、数据文件(从文件功能的⻆度来分类的)
2.1、程序文件
程序文件包括源文件(后缀为.c)目标文件(Windows环境后缀为.obj),可执行程序(Windows环境下后缀为.exe)
2.2、
数据文件
文件的內容不一定是程序,而是程序运行时读写的数据
3、二进制文件和文本文件
二进制文件就是以二进制的形式存储的文件,文本文件就是以ASCII码值的形式存储的文件
代码测试
//如何存储和打开文件呢
int main()
{
FILE* pf = fopen("text.txt", "wb");//wb--以二进制的形式写文件
if (pf == NULL)
{
perror("fopen");
return 1;
}
//写文件
int a = 15;
fwrite(&a, 4, 1, pf);//将1个4个字节大小的a写入到pf指向的文件中
fclose(pf);
pf = NULL;
return 0;
}
下图表示运行成功
右击源文件添加现有项
右击text.txt选择打开方式为二进制编译器即可观察存储是否正确
4、文件的打开和关闭
4.1、流和标准流
简单来说流就是运送字符的一个通道
标准流
stdin–标准输入流,在大多数环境中从键盘输入,例如scanf函数
stdout–标准输出流,在大多数环境中输出到显示器界面,例如printf函数
stderr–标准错误流,大多数环境中输出到显示器界面
以上三个流在程序启动时,是默认打开的状态,他们的类型是FILE*,通常称为文件指针,C语言中就是通过FILE*的文件指针来维护流的各种操作
4.2、文件指针
每个被使用的文件都会在内存中开辟一个相应的文件信息区,用来存放文件的相关信息,这些信息存放在一个结构体变量中,该结构体由系统声明,取名FILE
struct _iobuf
{
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
以上是VS2013编译环境提供的文件类型声明,不同环境下的文件类型声明可能不一样但是大同小异,使用者一般通过一个文件指针来维护所开辟的文件信息区,这样使用起来更方便,通过文件指针找到与其关联的文件
4.3、文件的打开和关闭
文件在读写之前应该先打开文件,使用结束之后要关闭文件,在编写程序的时候,在打开文件的同时都会返回一个FILE*的指针变量指向该文件,相当于建立了指针和文件的联系,使用fopen函数来打开文件,fclose函数来关闭文件
FILE* fopen(const char* filename,const char* mode);
int fclose(FILE* stream);
filename–表示文件的名字
mode–表示文件的打开方式
常见的文件打开方式
“r”–只读,打开一个已经存在的文件,读取里面的数据,如果文件不存在就会报错
“w”–只写,如果文件存在,清空文件的原数据并重新写入数据,文件如果不存在则创建该文件并写入数据
“a”–追加,如果文件存在,在该文件的原数据的基础上继续写入数据,不存在就创建并写入
“rb”–二进制的只读,本质上和"r"差不多就是读的形式是二进制
“wb”–二进制的只写,本质上和"w"差不多就是写的形式是二进制
“ab”–二进制的追加
5、文件的顺序读写
5.1、顺序读写的函数介绍
5.2、顺序读写函数的使用
int fgetc(FILE* stream) 一次读取一个字符
int main()
{
FILE* pf = fopen("text.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
char ch = fgetc(pf);
printf("%c", ch);
fclose(pf);
pf = NULL;
return 0;
}
int fptuc(int _Charter,FILE* stream) 一次写入一个字符
int main()
{
FILE* pf = fopen("text.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
char ch = 'a';
for (ch = 'a'; ch <= 'z'; ch++)
{
fputc(ch, pf);
};
fclose(pf);
pf = NULL;
return 0;
}
int fputs(const char* Buffer,FILE* stream)一次写入一行数据
Buffer–表示字符数组的首元素地址
int main()
{
FILE* pf = fopen("fputs.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fputs("hello world\n", pf);
fputs("cctv", pf);
fclose(pf);
pf = NULL;
return 0;
}
写入效果
char * fgets ( char * str, int num, FILE * stream ) 一次读取一行数据
str–存储数组的起始地址,最多存num-1个元素,还有一个位置放\0,stream–文件流
int main()
{
FILE* pf = fopen("fputs.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
char ch[20];
fgets(ch, 20, pf);
printf("%s", ch);
fclose(pf);
pf = NULL;
return 0;
}
int fscanf ( FILE * stream, const char * format, … ) 读取字符串中格式化的数据
struct stu
{
char name[10];
int age;
char address[20];
};
int main()
{
FILE* pf = fopen("fprintf.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
struct stu s= { 0 };
fscanf(pf, "%s %d %s", &s.name, &s.age, &s.address);
//将pf指向的文件中的格式化的数据读取到结构体s中
printf("%s %d %s", s.name, s.age, s.address);
fclose(pf);
pf = NULL;
return 0;
}
int fprintf ( FILE * stream, const char * format, … ) 将字符串中的格式化的数据写入文件指针指向的文件中
struct stu
{
char name[10];
int age;
char address[20];
};
int main()
{
FILE* pf = fopen("fprintf.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
struct stu s = { "zhangsan",12,"wuhan" };
fprintf(pf, "%s %d %s", s.name, s.age, s.address);//将格式化的数据写入pf指向的文件中
fclose(pf);
pf = NULL;
return 0;
}
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream )
从文件中读取大小为size,个数为count个的元素到ptr数组中
int main()
{
FILE* pf = fopen("text.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
char read[30];
fread(read, sizeof(char), 5, pf);
for (int i = 0; i < 5; i++)
{
printf("%c", read[i]);
}
fclose(pf);
pf = NULL;
return 0;
}
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream )
int main()
{
FILE* pf = fopen("fwrite.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
char write[] = "abcdefg";
fwrite(write, sizeof(char), 3, pf);
//将write数组中的前三个元素写入到pf指向的文件中
fclose(pf);
pf = NULL;
return 0;
}
5.3、对比scanf/fscanf/sscanf和printf/fprintf/sprintf
scanf是针对标准输入流stdin的格式化输入流函数
printf是针对标准输出流stdout的格式化输出流函数
fscanf针对所有输入流的格式化输入函数
fprintf针对所有输出流的格式化输出函数
sscanf将字符串中的格式化数据读取出来
sprintf将格式化的数据写入到字符数组中
//sprintf
int main_sprintf()
{
struct stu s = { "zhangsan",20,"wuhan" };
char string[50];
sprintf(string, "%s %d %s", s.name, s.age, s.address);
//将格式化的数据写入到字符数组string中
printf(string);
return 0;
}
int main_sscanf()
{
char arr[] = "I am 10 years old this year";
char str[30];
int i;
sscanf(arr, "%s %*s %d", str, &i);
//%*s -- 表示读取一个字符串,但不对其进行赋值,忽略这个读取结果,所以才能使得%d读取到10对i进行赋值
printf("%s %d", str, i);
return 0;
}
6、文件的随机读写
6.1、fseek
int fseek ( FILE * stream, long int offset, int origin )
offset–表示偏移量
origin–有三个取值SEEK_SET(文件的起始位置) 、SEEK_CUR (文件的当前位置)、SEEK_END(文件的末尾)
int main()
{
FILE* pf = fopen("text.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
char ch;
ch=fgetc(pf);
printf("%c\n", ch);
fseek(pf,2, SEEK_SET);//相对于文件起始位置偏移2
ch = fgetc(pf);//获取定位后的文件指针指向的字符
printf("%c\n", ch);
fclose(pf);
pf = NULL;
return 0;
}
6.2、ftell
long int ftell ( FILE * stream )
ftell – 返回文件指针相对于起始位置的偏移量
int main()
{
FILE* pf = fopen("text.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
char ch;
ch = fgetc(pf);//第一次读取
printf("%c\n", ch);
ch = fgetc(pf);//第二次读取
printf("%c\n", ch);
int a=ftell(pf);//此时文件指针相对于起始位置偏移量为2
printf("%d\n", a);
fclose(pf);
pf = NULL;
return 0;
}
6.3、rewind
void rewind ( FILE * stream ) 让文件指针返回到起始位置
int main()
{
FILE* pf = fopen("text.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
char ch;
ch = fgetc(pf);//第一次读取
ch = fgetc(pf);//第二次读取
rewind(pf);//让文件指针的位置返回到文件的起始位置
ch = fgetc(pf);//再次读取,读取到的就是第一次读取的字符
printf("%c\n", ch);
fclose(pf);
pf = NULL;
return 0;
}
7、文件读取结束的判定
feof—它是用来判断文件读取结束的原因是否是遇到了文件的末尾而结束
ferror—它是用来判断文件读取结束的原因是否是读取失败而结束
文本文件读取结束判断返回值是否为EOF或NULL
例如:
fgetc 读取失败或者是遇到文件的末尾都会返回EOF
fgets 读取失败或者是遇到文件的末尾会返回NULL
二进制文件读取结束判断返回值是否小于要读取的个数
例如:fread
代码案例展示:
文本文件读取结束判定读取结束的原因
int main()
{
FILE* pf = fopen("text.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
int i=fgetc(pf);
while ((i=fgetc(pf))!=EOF)
{
;
}
if (feof(pf))
{
printf("EOF");
}
else if (ferror(pf))
{
printf("NULL");
}
fclose(pf);
pf=NULL;
return 0;
}
二进制文件读取结束的判定
int main()
{
FILE* pf = fopen("text.txt", "rb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
char read[20];
int size=fread(read, sizeof(char), 3, pf);
if (size == 3)
{
printf("读取成功\n");
}
else
{
if (feof(pf))
printf("EOF\n");
else if (ferror(pf))
printf("NULL");
}
fclose(pf);
pf = NULL;
return 0;
}
8、文件缓冲区
#include <Windows.h>
int main()
{
FILE* pf = fopen("huanchongqu.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fputc('c', pf);
Sleep(10000);//让程序运行到下一步的速度迟缓10秒
//休眠时间打开文件,会发现里面还没有数据
fflush(pf);//刷新缓冲区,让缓冲区的数据写到文件中
Sleep(10000);
//此时打开文件,数据就又有了
fclose(pf);//在关闭文件时也会刷新缓冲区
pf = NULL;
return 0;
}
因为有了缓冲区的存在,在对文件读写数据时要刷新缓冲区或者在文件结束的时候关闭文件,否则会造成读写文件的问题