个人博客网址:https://ljsblog.com
文件操作(十一)
磁盘上的文件就是文件。
在程序设计中,我们所说的文件有两种:
- 程序文件:包括源程序文件(后缀为.c),目标文件(.obj),可执行程序(.exe)。
- 数据文件:文件的内容是程序运行是读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。
本篇以数据文件为主
文件名
为了区分计算机中不同的文件,而给每个文件设定一个指定的名称,就是文件名。
文件名由文件主名和扩展名组成。
文件类型
根据数据的组织形式,数据文件被称为文本文件或二进制文件。
文本文件:如果要求在外存上一ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件。
二进制文件:数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件。
字符在内存中以ASCII码的形式存储,数值型数据既可以用ASCII码的形式存储,也可以用二进制的形式存储。
如有整数10000,以ASCII码的形式输出到磁盘,每个字符一个字节,在磁盘中共占五个字节;以二进制的形式输出,则占4个字节。
文件缓冲区
ANSIC(美国国家标准协会对C语言发布的标准)标准采用“缓冲文件系统”处理的数据文件的。
缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块文件缓冲区。
从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上;从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。
缓冲区的大小根据C编译系统决定的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sRhlBNT1-1625747818063)(File-buffer.PNG)]
文件指针
文件指针即文件类型指针。
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件名,文件状态,文件位置等),这些信息被放在一个结构体变量中。该结构体类型有系统声明的,FILE。
每打开一个文件,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息。
一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用其起来更加方便。
创建一个FILE*的指针变量:
FILE* pf;
pf是一个指向FILE类型数据的指针变量,可以使pf指向某个文件的文件信息区(结构体变量),通过文件信息区中的信息就能够访问该文件,也就是说,可以通过文件指针变量能够找到与它关联的文件。
文件的打开和关闭
文件在读写前应先打开文件,使用结束后应关闭文件。
fopen函数打开文件,fclose关闭文件。
fopen函数
打开文件,在打开文件的同时,会返回一个FILE*的指针变量指向该文件,打开失败,返回空指针。
格式:
FILE *fopen(const char *filename, const char *mode)
filename:要打开的文件名称
mode:文件访问模式
fclose函数
关闭文件。
格式:
int fclose(FILE *stream)
stream:这是指向FILE对象的指针,该FILE对象指定了要被关闭的流。
例:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE* pf=fopen("test.txt","w");
//以写的形式打开test.txt文件,若没有该文件,则系统自动创建一个
if(pf==NULL)//判断打开文件是否失败
{
printf("%s\n",strerror(errno));//打印失败原因
return 0;
}
fclose(pf);//关闭文件
pf=NULL;
return 0;
}
文件访问模式
- “r”:打开一个用于读取的文件。该文件必须存在。
- “w”:创建一个用于写入的空文件。如果文件名称与已存在的文件相同,则会删除已有文件的内容,文件被视为一个新的空文件。
- “a”:追加到一个文件。写操作向文件末尾追加数据。如果文件不存在,则创建文件。
- “r+”:打开一个用于更新的文件,可读取也可写入。该文件必须存在。
- “w+”:创建一个用于读写的空文件。
- “a+”:打开一个用于读取和追加的文件。
文件的顺序读写
fputc
把一个无符号字符写入到指定文件中,并把位置标识符往前移动。
int fputc(int char, FILE *stream)
例:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE* pfw=fopen("test.txt","w");
if(pfw==NULL)
{
printf("%s\n",strerror(errno));
return 0;
}
fputc('b',pfw);
fputc('l',pfw);
fputc('o',pfw);
fputc('g',pfw);
fclose(pfw);
pfw=NULL;
return 0;
}
/*test.txt内容:
blog
*/
fgetc
从指定的文件一个无符号字符,并把位置标识符往前移动。
格式:
int fgetc(FILE *stream)
例:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE* pfr=fopen("test.txt","r");
if(pfr==NULL)
{
printf("%s\n",strerror(errno));
return 0;
}
printf("%c",fgetc(pfr));
printf("%c",fgetc(pfr));
printf("%c",fgetc(pfr));
printf("%c",fgetc(pfr));
fclose(pfr);
pfr=NULL;
return 0;
}
/*
blog
*/
fgets
从指定的文件读取一行。
格式:
char *fgets(char *str, int n, FILE *stream)
str:指向字符数组的指针,该数组存储了要读取的字符串。
n:这是要读取的最大字符数(包括最后的空字符)。通常是使用以str传递的数组长度。
stream:这是指向FILE对象的指针,该FILE对象标识了要从中读取字符的流。
例:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
char s[100]={0};
FILE* pf=fopen("test.txt","r");
if(pf==NULL)
{
printf("%s\n",strerror(errno));
return 0;
}
fgets(s,100,pf);
printf("%s\n",s);
fclose(pf);
pf=NULL;
return 0;
}
/*
blog
*/
注:fgets读取时会将换行也读进来
fputs
把字符串写入到指定的流stream中。
格式:
int fputs(const char *str, FILE *stream)
例:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
char s[100]={0};
FILE* pf=fopen("test.txt","w");
if(pf==NULL)
{
printf("%s\n",strerror(errno));
return 0;
}
fputs("my",pf);
fputs("blog",pf);
fclose(pf);
pf=NULL;
return 0;
}
/*test.txt内容:
myblog
*/
sprintf
sprintf是把格式化数据输出成字符串
格式:
int fprintf(FILE *stream, const char *format, …)
例:
#include <stdio.h>
#include <string.h>
#include <errno.h>
struct A
{
int i;
char s[10];
float f;
};
int main()
{
struct A a1={10,"myblog",3.14};
FILE* pf=fopen("test.txt","w");
if(pf==NULL)
{
printf("%s\n",strerror(errno));
return 0;
}
fprintf(pf,"%d %s %f",a1.i,a1.s,a1.f);
fclose(pf);
pf=NULL;
return 0;
}
/*test.txt内容:
10 myblog 3.140000
*/
sscanf
sscanf是从字符串中读取格式化的数据
格式:
int fscanf(FILE *stream, const char *format, …)
例:
#include <stdio.h>
#include <string.h>
#include <errno.h>
struct A
{
int i;
char s[10];
float f;
};
int main()
{
struct A a1={0};
FILE* pf=fopen("test.txt","r");
if(pf==NULL)
{
printf("%s\n",strerror(errno));
return 0;
}
fscanf(pf,"%d %s %f",&(a1.i),a1.s,&(a1.f));
printf("%d %s %f\n",a1.i,a1.s,a1.f);
fclose(pf);
pf=NULL;
return 0;
}
/*
10 myblog 3.140000
*/
文件的随机读写
fseek和ftell
fseek根据文件指针的位置和偏移量来定位文件指针。
格式:
int fseek(FILE *stream, long int offset, int origin);
fell返回文件指针相对于起始位置的偏移量。
格式:
long int ftell(FILE *stream)
例:
//文件内容为:myblog
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
int i=0;
FILE* pf=fopen("test.txt","r");
if(pf==NULL)
{
printf("%s\n",strerror(errno));
return 0;
}
fseek(pf,-3,SEEK_END);//SEEL_END表示文件末尾
i=ftell(pf);
printf("%d\n",i);//3
fclose(pf);
pf=NULL;
return 0;
}
rewind
让文件指针回到起始位置。
void rewind(FILE *stream)
例:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
int i=0;
FILE* pf=fopen("test.txt","r");
if(pf==NULL)
{
printf("%s\n",strerror(errno));
return 0;
}
fseek(pf,-3,SEEK_END);
rewind(pf);
i=ftell(pf);
printf("%d\n",i);//0
fclose(pf);
pf=NULL;
return 0;
}
文件结束判断feof函数
feof函数应用于当文件读取结束时,判断是读取失败结束,还是遇到文件尾结束。
- 文本文件读取是否结束,看返回值是否为EOF(fgetc)或NULL(fgets)。
- 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。
int feof(FILE *stream)
例:
//文件内容为myblog
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
int i=0;
char s;
FILE* pf=fopen("test.txt","r");
if(pf==NULL)
{
printf("%s\n",strerror(errno));
return 0;
}
while((s=fgetc(pf))!=EOF)
{
putchar(s);
}
printf("\n");
if(ferror(pf))
//ferror:如果设置了与流关联的错误标识符,该函数返回一个非零值,否则返回一个零值。
{
printf("error\n");
}
else if(feof(pf))
//feof:如果设置了与流关联的文件结束标识符时,该函数返回一个非零值,否则返回零。
{
printf("end of file\n");
}
fclose(pf);
pf=NULL;
return 0;
}
/*
myblog
end of file
*/