C语言文件的相关操作
文件的介绍
文件的打开和关闭
字符读写函数
字符串读写函数
格式化的读写函数
二进制的读写函数
文件定位
标准I/O是会分配缓存的。
文件的介绍:
1:文件的概念
所谓“文件”是指一组相关数据的有序集合,这个数据集合有一个名称,叫做”文件”,如:源文件,目标文件,可执行文件,头文件,二进制文件,.java,.o,等等一系列文件
文件一般都是驻留在外部介质(磁盘中),在使用的时候才去将文件读入到内存中来;
分类:
从用户角度分类可以分为普通文件和设备文件两种;
普通文件:存放在磁盘上或其他存储空间上的文件
设备文件:外接设备称为设备文件,如打印机等
从编码方式来分类的话可以分为文本文件和二进制文件两种
文本文件是以ASCII编码的形式进行存放,一个字节存放一个字符,文本文件的每一个字节存放一个ASCII编码,代表一个字符,占用存储空间多,转换成二进制速度比较慢,但是便于读懂
二进制文件是以补码的形式存放,二进制文件就是把数据以二进制的形式存放在文件中,其占用的存储空间比较小,不需要转换。
2文件系统的分类
目前C语言所使用的磁盘文件系统分为缓冲文件系统和非缓冲的文件系统
缓冲文件系统 :系统会在内存区域开辟一个缓存用来做为中继存储结构,先将文件内容读取到缓存中去,然后再从缓存中读取,包括写文件也是一样的,先到缓存,再从缓存到文件。(缓存的大小决定了读文件读多少的最终临界),缓存的设置减少对磁盘的实际读写次数,从而来提高读写的效率
非缓冲文件系统:系统不会提供一个缓存,程序对直接对文件进行操作(可以用户自己来设置)
在程序中我们可以自己根据需要去设置一些缓存,来保存读写的数据,存储到自己缓存中,然后从缓存中再进行读写;
用户在程序中为每个文件读写设定一个缓冲区域
3:文件流的概念
概念:在C语言对文件的操作最终转化为对缓存中的数据进行操作,流实际上就是内存中的一种具有自动顺序操作的特殊数据,流如同流动的河水一样,它具有源和目标地
分类:
按方向来分:以缓存为中心:流入缓存的是输入,流出的是输出流
1:输入流:从文件读取的流
2:输出流:将数据输出到文件的流
按流的数据内容:根据文本文件和二进制文件来区分为文件流和二进制流
1:文件流,文本流就是流动的字符序列
2:二进制流:流动的二进制序列
三个标准流:
1:标准输入流 stdin(0):针对标准输入键盘
2:标准输出流stdout(1):针对标准输出屏幕
3:标准错误流 stderr(2):针对标准输出屏幕
文件的打开和关闭
1:文件类型结构体FILE和文件指针
文件类型结构体:缓冲文件系统中会为每个正在使用的文件在内存开辟文件信息区域,文件信息用系统定义的名为FILE的结构体来描述
typedef struct{
short level; /*缓冲区域满/空程度**/
unsigned flags; /**文件状态标志*/
char fd;/*文件描述符*/
unsigned char hold;/**若无缓冲区,不读取字符*/
short bsize;/*缓冲区大小*/
unsigned char *buffer;/*数据传送缓冲区的位置(重要)*/
unsigned char *curp;/*当前读写位置(重要)*/
unsigned istemp;/*临时文件只是*/
short token;/*用作无效检测*/
}FILE;
文件指针的定义FILE*类型的统称为流指针
FILE *指针变量名
文件指针实际上是一个文件类型结构体指针
通过文件指针即可以找到存放某个文件信息的文件类型结构体变量,然后按结构体变量提供的信息找到该文件,从而对文件进行操作
打开文件的似乎后会返回一个FILE*
而关闭文件的时候会释放出FILE *
2:文件打开
使用FILE *fopen(const char *path, const char *mode);函数来打开文件
path:要打开的文件路径
mode:使用文件方式
返回:如果成功,返回FILE*,失败返回NULL;
使用如:FILE *f;
f = fopen("123.txt","w+");
if(f ==NULL)
printf("open failed");
else
printf("open success");
符号定义:
r:read,读取的时候如果文件不存在,可能就会出错
w:write/建立新文件
a:append/建立新文件
+:追加
t:text
b:binary
符号之间随机组合最终形成mode
3:文件关闭
使用int fclose(FILE *fp);来关闭文件
int ret = fclose(f);
if(ret == 0)
printf("close success");
else
printf("close fail");
关闭成功返回0,失败返回EOF(-1)
注意:不关闭文件有可能会丢失数据,调用fclose之后,系统会刷新缓存,将缓存区域中的数据全部刷新到文件中去。然后再去释放文件
4:ftell函数
int ftell(FILE* f);
功能:测试的当前文件的读写位置
返回:测试成功返回文件位置指针所在的位置,(当前读写位置距离文本开头的字节数目),失败返回-1
文件位置指针:只想当前读写位置的指针
如:
FILE * f = fopen("a.txt","w");
long pos = ftell(f)
文件打开关闭的代码如下所示:
#include<stdio.h>
#include<stdlib.h>
int main(int argc,char *argv[]){
FILE *fp;
fp = fopen("/etc/passwd","r");
if(fp == NULL){
printf("open file error!\n");
exit(EXIT_FAILURE);
}
int pos = ftell(fp);
printf("pos:%d\n",pos);
int result = fclose(fp);
if(result == 0){
printf("close file success \n");
}else if(result == EOF){
printf("close file error \n");
exit(EXIT_FAILURE);
}else{
}
return 0;
}
字符读写函数
1:getchar和putchar函数(标准输入输出)
getchar函数:
int getchar();
功能:从标准输入读取一个字符
返回:成功返回读取的字符,失败就会返回EOF
putchar函数
int putchar(int ch);
功能:将一个字符写入到标准输出
返回:成功返回写入的字符ch,失败返回EOF
2:fgetc和fputc函数
fgetc函数:
int fgetc(FILE *f);
功能:从fp指向的文件中读取一个字符
返回:成功会返回读取的字符,失败或读到文件尾部返回EOF(-1);
可以对标准输入进行操作;
fgetc(stdin);
fputc函数:
int fputc(int ch,FILE *fp);
功能:把一个字符ch写如到fp指向的文件中去,
返回:成功返回写入的字符,失败返回EOF
可以针对标准输出:
fputc(int ch,stdout);
ungetc函数:
int ungetc(int c,FILE *fp);
功能:撤销一个字符
返回:成功则返回撤销的字符,失败返回EOF
下面是字符读取函数的代码部分:
#include<stdio.h>
#include<stdlib.h>
int main(int argc,char *argv[]){
/**使用getchar和putchar来进行标准的输入和输出*/
// int ch;
// while((ch = getchar()) != '\n'){
// putchar(ch);
// }
// putchar("\n");
/**利用fgetc和fputc来进行标准输入输出*/
// int ch2;
// while((ch2 = fgetc(stdin))!= '\n'){
// fputc(ch2,stdout);
// }
// fputc('\n',stdout);
FILE *fp;
//打开或创建一个文件,写文件
fp = fopen("new.txt","w+");
if(fp != NULL){
int ch2 ;
while((ch2 = fgetc(stdin))!= '\n'){
fputc(ch2,fp);
}
fputc('\n',fp);
}else{
printf("open file failed!\n");
}
int file_close_result = fclose(fp);
if(file_close_result == 0){
printf("close success\n");
}else{
printf("close failed\n");
}
return 0;
}
字符串读写函数
1:fgets和fputs函数(gets和puts函数)
fgets函数:
char *fgets(char *str,int size,FILE *fp);
功能:从fp所指向的文件中 最多读size-1个字符,
放入str指向的字符数组中,如果在读入的size-1
个字符结束前遇到换行符或者EOF,读入即结束,字符串读入后在最后一位加'\0'字符
返回:返回的就是str这个指针,失败返回NULL,
同时也可以针对标准输入:
与scanf相互对比的话,scanf在有空格的情况下是读写不了的
fputs函数
int fputs(char *str,FILE *fp);
功能:把str指向的字符串或字符数组写入到fp指向的文件中去,
返回:成功返回0,失败则返回EOF
也可以针对标准输入操作
利用字符串读写打造我们自己的cat函数和cp函数
#include<stdio.h>
#include<stdlib.h>
int main(int argc,char *argv[]){
if(argc != 3){
printf("usage:%s source target\n",argv[0]);
exit(EXIT_FAILURE);
}
FILE *source_file_pointer;
FILE *des_file_pointer;
source_file_pointer = fopen(argv[1],"r");
if(source_file_pointer == NULL){
printf("open source file is error\n");
exit(EXIT_FAILURE);
}
des_file_pointer = fopen(argv[2],"w");
if(des_file_pointer == NULL){
printf("open or create des file error\n");
exit(EXIT_FAILURE);
}
char buffer[100];
char *sp;
while((sp = fgets(buffer,sizeof(buffer),source_file_pointer)) != NULL){
fputs(buffer,des_file_pointer);//打造我们自己的cp函数
fgets(buffer,stdout); //打造我们自己的cat函数
}
int source_file_close = fclose(source_file_pointer);
if(source_file_close == 0){
printf("source_file close success\n");
}else{
printf("source file close failed\n");
}
int des_file_close = fclose(des_file_pointer);
if(des_file_close == 0){
printf("des file close success \n");
}else{
printf("des file close failed \n");
}
return 0;
}
格式化的读写函数
1:fscanf和fprintf函数
scanf和printf也是格式化的输入输出函数
fscanf函数
int fscanf(FILE *fp,const char *format...);
fprintf函数
int fprintf(FILE *fp ,const char *format...);
功能:按format的格式对fp指向的文件进行I/O操作
返回:成功返回I/O字节数,失败或到文件尾返回EOF
可以针对标准输入和输出进行操作;
示例代码;
fscanf(fp,"%d%f",&i,&j);
若文件中有5和5.5,则会将5放入i中,5.5放入j中去
fprintf(fp,"%c",ch);
将字符ch输入到fp所指向的文件中去
适合场景。读写文件中行是统一格式的形式:
如:zhangsan 88 90 70
lisi 74 82 72
zzf 60 70 80
这样的格式的话,读写可以使用fprintf和fscanf,而且便于后续数据的处理
学会用面向对象的思想去读取文件
#include<stdio.h>
#include<stdlib.h>
struct student{
int xh;
char name[100]; //切记不能用字符指针,因为字符指针指向的是常量,这个时候容易出现段错误,因为指针是要有指向的,字符指针的定义没有指向,所以导致在scanf过程中出现野指针现象,如果声明了//char *name;那么在初始化的时候,一定要赋予字符指针指向如:char name2[100]; stu.name = name2;
int math_score;
int chinese_score;
int english_score;
float average;
char grade;
};
typedef struct student Stu;
char getGrade(float average);
int main(int argc,char *argv[]){
if(argc != 3){
printf("缺少参数\n");
exit(EXIT_FAILURE);
}
FILE *source_file_pointer;
FILE *des_file_pointer;
source_file_pointer = fopen(argv[1],"r");
printf("argv[1]:%s\n",argv[1]);
if(source_file_pointer == NULL){
printf("open source file error\n");
exit(EXIT_FAILURE);
}
des_file_pointer = fopen(argv[2],"w");
printf("argv[2]:%s\n",argv[2]);
if(des_file_pointer == NULL){
printf("open des file error\n");
exit(EXIT_FAILURE);
}
Stu stu = {0,'\0',0,0,0,0.0,'A'};
printf("%d %s %d %d %d %f %c\n",stu.xh,stu.name,stu.math_score,stu.chinese_score,stu.english_score,stu.average,stu.grade);
while(fscanf(source_file_pointer,"%d %s %d %d %d",&stu.xh,stu.name,&stu.math_score,&stu.chinese_score,&stu.english_score) != EOF){
stu.average = (stu.math_score +stu.chinese_score+stu.english_score)/3.0;
printf("average:%f\n",stu.average);
stu.grade = getGrade(stu.average);
printf("%c\n",stu.grade);
fprintf(des_file_pointer,"%s %f %c\n",stu.name,stu.average,stu.grade);
}
int source_close_result = fclose(source_file_pointer);
if(source_close_result == 0){
printf("close source file success\n");
}else{
printf("close source file failed\n");
exit(EXIT_FAILURE);
}
int des_close_result = fclose(des_file_pointer);
if(des_close_result == 0){
printf("close des file success\n");
}else{
printf("close de file failed\n");
exit(EXIT_FAILURE);
}
return 0;
}
2:sscanf和sprintf函数
sscanf和sprintf函数:
int sscanf(char const *str,char const *format .,..);
int sscanf(char const *str,char const *format,...);
功能:按format格式对str指向的字符数组进行I/O操作,
读写的源和目的都是字符串
返回:功能返回I/O字节数,失败返回EOF
实例代码:
char *str = "tom 100";
int id;
char name[50];
sscanf(str,"%s %d",name,%id);
char stu[100];
sprintf(stu,"%d %s",id,name);
二进制的读写函数(读取二进制文件)
1:fread和fwirte函数
fread和fwite函数:
int fread(void *buffer,int num_bytes,int count ,FILE *fp);
int fwrite(void * buffer,int num_bytes,int count,FILE *fp);
功能:读取数据块,一般用于二进制文件的输入和输出
返回:成功返回读写的元素个数,失败或到文件尾部返回EOF
fread > 0 说明读写成功
参数:
buffer:一个指向要进行输入输出数据存储区的通用指针
num_bytes:每个要读写的元素的字节数目
count:要读写的元素个数
fp:要读写的文件指针
补充:
1:对于二进制文件feof可以判断是否读到文件结尾:
!feof(spIn) //判断是否读取到了文件的结尾部分,但是是需要提前读取一次的
size_t re = fread();
2:fread和fwrite在读写二进制文件的时候效率是更高的。
在内存和磁盘频繁交换数据的情况下,尽量使用fread和fwrite函数
二进制读写的相关代码如下所示:
#include<stdio.h>
#include<stdlib.h>
typedef struct student{
int xh;
char name[20];
int math_score;
int english_score;
int chinese_score;
}Stu;
void text_to_bin();
void bin_to_text();
/**
*代码实例是将文本文件写入二进制文件,然后从二进制文件中读取,再写入到
*文本文件中去
*/
int main(int argc,char *argv[]){
if(argc !=4 ){
printf("缺少参数\n");
exit(EXIT_FAILURE);
}
text_to_bin(argv);
bin_to_text(argv);
return 0;
}
void text_to_bin(char *argv[]){
FILE *source_file_pointer;
FILE *des_file_pointer;
Stu stu = {1,"demo",0,0,0};
source_file_pointer = fopen(argv[1],"r");
if(source_file_pointer == NULL){
printf("open text source file failed\n");
exit(EXIT_FAILURE);
}
des_file_pointer = fopen(argv[2],"wb");
if(des_file_pointer == NULL){
printf("open bin source file failed\n");
exit(EXIT_FAILURE);
}
while(fscanf(source_file_pointer,"%d %s %d %d %d",&stu.xh,stu.name,&stu.math_score,&stu.english_score,&stu.chinese_score) != EOF ){
fwrite(&stu,sizeof(stu),1,des_file_pointer);
}
int source_close_result = fclose(source_file_pointer);
if(source_close_result == EOF){
printf("close source file failed\n");
exit(EXIT_FAILURE);
}else{
printf("close source file success\n");
}
int des_close_result = fclose(des_file_pointer);
if(des_close_result == EOF){
printf("close des file failed\n");
exit(EXIT_FAILURE);
}else{
printf("close des file success\n");
}
}
void bin_to_text(char *argv[]){
FILE *bin_source_file_pointer;
FILE *text_des_file_pointer;
Stu stu = {1,"z",0,0,0};
bin_source_file_pointer = fopen(argv[2],"rb");
if(bin_source_file_pointer == NULL){
printf("open bin_source_file failed\n");
//perror(argv[2]);
exit(EXIT_FAILURE);
}else{
printf("open bin_source_file success\n");
}
text_des_file_pointer = fopen(argv[3],"w");
if(text_des_file_pointer == NULL){
printf("open text des file failed\n");
perror(argv[3]);
exit(EXIT_FAILURE);
}else{
printf("open text_des file success\n");
}
while(fread(&stu,sizeof(stu),1,bin_source_file_pointer)){
fprintf(text_des_file_pointer,"%d %s %d %d %d\n",stu.xh,stu.name,stu.math_score,stu.english_score,stu.chinese_score);
}
int bin_source_file_close_result = fclose(bin_source_file_pointer);
if(bin_source_file_close_result == EOF){
printf("close bin_source_close_file error\n");
exit(EXIT_FAILURE);
}else{
printf("close bin_source_close_file success\n");
}
int text_des_file_close_result = fclose(text_des_file_pointer);
if(text_des_file_close_result == EOF){
printf("close text_des_file error\n");
exit(EXIT_FAILURE);
}else{
printf("close text_des_file success\n");
}
}
文件定位
1:概念
在一般对文件的读写都是顺序读写,即读写文件只能从开头开始,顺序读写各个数据,但是在实际问题中我们经常是要求只去读写文件中的某一个部分,为了解决这个问题可移动文件内部的文件位置指针到需要读写的位置,再进行读写,这种读写叫做随机读写
实现随机读写的关键是要按照要求移动文件位置指针,这称作文件定位
2:fseek函数
fseek函数:
int fseek(FILE * fp,long offset,int whence);
功能:使fp所指向的位置指针重置到指定位置(从whence位置移动offset个字节)
返回:成功返回0,失败返回EOF(-1)
offset:位移量,表示移动的字节数(偏移量)
whence的值:
SEEK_SET 文件首 0 offset非负
SEEK_CURRENT 文件当前的位置 1 offset正负都可以
SEEK_END 文件尾部 2 offset正负都是可以的
如:
fseek(fp,100,SEEK_SET);
3:其他函数
1:rewind函数
void rewind(FILE *fp);
功能:使文件位置指针重新返回文件首部
与fseek进行联用,对文件位置指针进行重置
2:remove函数
int remove(const char * filename);
功能:用于删除指定的函数
3:fflush函数:
void fflush(FILE *fp);
功能:刷新缓冲区域,如果打开文件进行读操作,该函数将会清空文件的输入缓冲区,如果打开文件进行读写操作时,该函数将文件的输出缓冲区内容写入文件中去;
最后在给大家附图一张,便于记忆:
最后谢谢大家的访问:欢迎持续访问