文本文件就是把每一个字符的ASCII码存放到文件中
二进制文件就是把数据对应的二进制形式存储到文件中
FILE* fopen(const char* filename,const char* mode);
(1)函数参数:
filename:文件名
mode:文件打开的权限,权限包括如下:
模式 | 含义 |
“r” | 只读 |
“w” | 只写 |
“a” | 文件末尾只写 |
“r+” | 在读的基础上增加可写的功能 |
“w+” | 向文件中写入数据,然后可读取文件的内容 |
“a+” | 文件末尾只写,并且可以读 |
“rb” | 二进制只读 |
“wb” | 二进制只写 |
“ab” | 二进制文件尾只写 |
“rb+” | 二进制文件在读的基础上增加可写的功能 |
“wb+” | 向二进制文件中写入数据,然后可读取文件的内容 |
“ab+” | 二进制文件末尾只写,并且可以读 |
(2)返回值:
打开成功返回FILE类型的指针,打开失败返回NULL
int fclose(FILE* stream)
(1)函数参数:
stream:指向需要关闭流的指针
(2)返回值:
关闭成功返回0,关闭失败返回-1
int sprintf(char* str,const char* format,……)
(1)函数参数:
str:指向缓冲区的指针,是将format里面的格式化字符串写入此缓冲区
format:格式化字符串,同printf的格式规范相同
……:附加参数
(2)返回值:
成功返回写入的字符总数,失败返回负数
(3)示例:
#include
int main()
{
char buff[128];
int val=100;
sprintf(buff,"val = %d\n",val);//此时会将字符串"val = 100\n\0"写入buff数组中,一共占11字节
return 0;
}
int fprintf(FILE* stream,const char* format,……)
(1)函数参数:
stream:将format里面的格式化字符串写入到stream流中
format:格式化字符串
……:附加参数
(2)返回值:
成功返回写入的字符总数,失败返回负数
(3)示例:
#include
int main()
{
int ar[]={12,23,34};
int n=sizeof(ar)/sizeof(ar[0]);
FILE* fd=open("text.txt","w");
if(fd==nullptr)
{
printf("打开文件失败");
exit(EXIT_FAILURE);
}
for(int i=0;i
int fscanf(FILE* stream,const char* format,……)
(1)函数参数:
stream:从stream这个流中读取数据
format:格式化字符串,遵循scanf的格式规范
……:附加参数
(2)返回值
成功返回接收参数的数量,失败返回-1
(3)示例:
#include
#define SIZE 10
int main()
{
FILE* fd=fopen("text.txt","r");
if(fd==nullptr)
{
printf("打开文件失败");
exit(EXIT_FAILURE);
}
const int n=10;
int br[n]={0};
for(int i=0;i
size_t fwrite(const void* ptr,size_t size,size_t count,FILE* stream);
(1)函数参数:
prt:从ptr指向的地址获取内容从而写入到stream这个流中
size:单位元素的大小
count:总共的元素数
stream:指向被写入的流
(2)返回值:
成功返回写入元素的个数,如果失败,返回的元素个数会小于count
(3)示例:
#include
int main()
{
FILE* fdw=fopen("text,txt","wb");
if(fdw==nullptr)
{
printf("打开文件失败");
exit(EXIT_FAILURE);
}
int ar[]={12,23,34,45};
int n=sizeof(ar)/sizeof(ar[0]);
//将ar这个数组里面的所有元素以二进制的形式写入到fdw所指向的文件中
fwrite(ar,sizeof(int),n,fdw);
fclose(fdw);
fdw=nullptr;
return 0;
}
size_t fread(const void* ptr,size_t size,size_t count,FILE* stream);
(1)函数参数:
prt:从stream这个流中读取内容存放到ptr所指的地址
size:单位元素的大小
count:总共的元素数
stream:需要读取内容的流
(2)返回值:
成功返回读取元素的个数,如果失败,返回的元素个数会小于count
(3)示例:
#include
int main()
{
FILE* fdr=fopen("text,txt","rb");
if(fdr==nullptr)
{
printf("打开文件失败");
exit(EXIT_FAILURE);
}
int n=10;
int ar[n]={0};
//读取fdr所指向的二进制文件,读取sizeof(int)*n个字节,一次存放到ar数组中
fread(ar,sizeof(int),n,fdr);
fclose(fdr);
fdr=nullptr;
return 0;
}
缓冲文件就是指从内存向磁盘输出数据先存放到缓冲区,装满后再放到磁盘。反向亦如此。
非缓冲文件就是直接将数据从内存输出到磁盘,中间不经过缓冲区
int fflush(FILE* stream)
(1)函数参数:
stream:指向输出流或者更新流,fflush函数会把任何未被写入的数据写入stream指向的文件,比如stdout。
(2)返回值:
如果成功刷新或者指定的流没有缓冲区,又或者以只读可打开文件的时候返回0
返回EOR(表示-1)则会指出一个错误
(3)常见用法:
fflush(stdin):刷新标准输入缓冲区,把标准输入缓冲区里面的数据丢弃掉(注释:并非所有编译器都支持此功能,因为C/C++的标准里面并没有定义此用法,所以使用fflush(stdin)并不是正确的方式)
fflush(stdout):刷新标准输出缓冲区,把输出缓冲区里面的东西打印到标准输出设备上
之所以需要把缓冲区和流相关的目的在于:使用fopen\fread\fwrite\fprintf\fscanf函数的时候,会直接在内存中操作,减少内存到磁盘IO读写的操作次数,从而提高系统利用率。比如程序设计到使用数据库、视频、音频等大量爆发式磁盘到内存的IO情况,可以使用setvbuf优化内存IO。需要注意的是:linux\windows会自动处理这些问题。
(1)函数功能:
设置流操作的内部缓冲区,长度至少为BUFSIZE个字符,这个函数应该在打开流后,立即调用,在任何对该流做输入输出前
需要注意一下几点:
①如果buffer不为空,等价于setvbuf(stream,buffer,_IOFBF,BUFSIZE)
②如果buffer为空,则等价于setvbuf(stream,NULL,_IONBF,0)
③避免需要使用缓冲区的内容时,因为作用域的原因导致缓冲区被释放了,例如:
int main()
{
char buff[128];
setbuf(stdout,buff);
int x;
while(x=(getchar())!=EOF)
{
putchar(x);//将输入的内容先从放到buff的缓冲区中,然后等到缓冲区满再输出
}
}
上述程序是错误的,原因是如果缓冲区buff没有被放满,就不会输出,但是由于输入的x=-1,退出循环,程序结束,就会将buff里面的内容释放,所以无法将之前输入的内容正常输出。因此应该注意缓冲区的生存期。
(2)函数参数:
stream:要设置缓冲区的文件流
buffer:指针指向需要用到的缓冲区,如果指向NULL,表示不需要缓冲区
(3)示例:
int main()
{
char buff[1024];
FILE* fd=open("text.txt","w");
int a=100,b=200;
//把变量a,b通过字符串的形式先写入缓冲区buff中,然后再从缓冲区中写入到fd所指向的文件内部
setbuf(fd,buff);
fprintf(fd,"a = %d,b = &=%d\n",a,b);
fclose(fd);
fd=nullptr;
return 0;
}
(1)函数功能:
此函数的功能和setbuf差不多,只不过由于参数的不同,所以setvbuf可以指定缓冲区的大小
使用此函数还需要注意如下几点:
①当buffer为空,可以通过mode、size来重置内部缓冲区的大小
②当buffer不为空,可以通过mode、size来设置提供的buffer这个缓冲区的大小
③这个函数应该在打开流后,立即调用,在任何对该流做输入输出前
(2)函数参数:
stream:要设置缓冲的文件流
buffer:指向缓冲区
mode:缓冲模式
_IOFBF:全缓冲(当缓冲区满时在想流写入数据)
_IOLBF:行缓冲
_IONBF:无缓冲
size:缓冲区的大小
(3)返回值:
成功返回0,失败返回非零
long ftell(FILE* stream)
(1)参数:
stream:要检验的文件流
(2)返回值:
成功时返回文件位置指示器,失败则为-1L
int fgetpos(FILE* stream,fpos_t *pos)
(1)函数作用:
将stream文件流的位置指针保存到pos这个位置变量中
(2)参数:
stream:要检验的文件流
pos:指向要存储文件位置指示器到fpos_t对象的指针
(3)返回值:
成功返回0
失败返回非零值
(4)示例:
int main()
{
//打开text.txt文件,文件事先会有一定的内容
FILE* fd=fopen("text,txt","w");
fpos_t pos;
//使用fgetpos函数取得文件指针的位置,并存到Pos中,此时pos=0
fgetpos(fd,pos);
printf("当前的文件指针在第%ld个字节",pos);
//将文件位置偏移3,也就是指向文件的第4个字节
fseek(fd,3,0);
//此时pos的值为3
fgetpos(fd,pos);
printf("当前的文件指针在第%ld个字节",pos);
fclose(fd);
return 0;
}
可以用fseek()和ftell()来计算文件的长度
#include
int main()
{
FILE* fd=fopen("text,txt","r");
fseek(fd,0,SEEK_END);//将文件描述符移动到尾部
ftell(fd);//得出文件描述符的位置就是文件的大小
close(fd);
return 0;
}
(1)函数参数:
stream:要修改的文件流
offset:相对origin所移动的字符数
origin:offset以origin为基础位置进行移动,包括:
SEEK_SET:文件开头
SEEK_CUR:当前位置
SEEK_END:文件结尾
(2)返回值
成功时返回0,失败返回非0
(3)示例:
fseek(fd,100L,SEEK_SET):把fd指针移动到从文件开始位置计算往后100个字节的地方
fseek(fd,100L,SEEK_CUR):把fd指针移动到从文件当前位置计算往后100个字节的地方
fseek(fd,0L,SEEK_END):定位到文件结尾处
fseek(fd,-100L,SEEK_END):从文件结尾回退100字节
(1)函数参数:
stream :要修改的文件流
pos:指向fpos_t对象的指针,用作文件位置指示器的新值
(2)函数返回值:
成功返回0,否则非零
void rewind(FILE* stream)
(1)函数作用:
移动文件描述符到文件的开始位置,等价于fseek(fd,0,SEEK_SET);
void clearerr(FILE* stream)
(1)函数作用:
把文件结束符EOF和错误标识符从非0值变为0值
(2)函数参数:
stream:要重置错误标志的文件流
(3)示例:
int main()
{
FILE* fd=fopen("text.txt","r");//打开一个文件,这个文件里面是有内容的
//如果此文件的结尾不是EOF结尾,就会退出程序不再执行后序操作
assert(feof(fd));
int ch;
//读到文件结尾遇到EOF就退出循环
while((ch=fgetc(fd))!=EOF)
{
printf("%c",ch);
}
clearerr(fd);//将错误标志从非0变为0
//如果没有设置文件结束符eof就会返回0,否则返回非0,上面将EOF设置为0,说明此文件没有课文件结束符,因此feof(fd)会返回0,执行else语句
if(feof(fd))
{
printf("文件有结束符\n");
}
else
{
printf("文件没有结束符\n");
}
}
int feof(FILE* stream)
(1)函数作用:
检查是否抵达文件流的结尾
(2)函数参数:
stream:要检验的文件流
(3)返回值:
如果已经到结尾返回非零值,否则返回0
int ferror(FILE* stream)
(1)函数作用:
检查给定文件流的错误
(2)返回值:若文件流已经出现错误则返回非零值,否则返回0
(3)示例:
#include
#include
int main()
{
FILE* fd=fopen("text.txt","r");
int c;//使用
while((c=fgetc(fd))!=EOF)
{
putchar(c);
}
//ferror()与feof()分别用来区别不同的错误条件
if(ferror(fd))
{
printf("读取的时候发生错误\n");
}
else if(feof(fd))
{
printf("成功读取到文件末尾\n");
}
fclose(fd);
}
void perror(const char* s)
(1)函数作用:
用来将上一个函数发生错误的原因输出到标准设备stderr。然后会打印出:s所指向的字符串+错误原因字符串。
int remove(const char* fname);
(1)函数作用:
删除fname这个文件名所对应的文件
(2)函数参数:
fname:待删除文件的名字(包含路径)
(3)返回值:
成功返回0,失败返回非0
int rename(const char* old_filename,const char* new_filename);
更改文件的名字,前面的参数是原来名字,后面的参数是更改之后的文件名字
(1)函数作用:
创建唯一的临时二进制文件(类型为wb+),并打开此文件
(2)返回值:
成功返回文件流,失败返回空
(1)函数作用:
生成唯一临时文件名
使用fopen创建临时文件,文件关闭,使用remove函数来删除该文件
(2)函数参数:
s指向一个char类型的数组,这个数组里面存放临时文件的名称,如果s为空,就会使用一个内部静态数组来存储临时名称。
(3)返回值:
成功时返回文件名字的指针,失败返回空
int fgetc(FILE* stream)
int getc(FILE* stream)
(1)返回值:
成功时为获取的字符,失败返回EOF
(2)fegtc和getc的区别:
fgetc()是一个函数,getc()是一个宏,宏调用的时候参数不能是副作用表达式(副作用表达式:表达式执行完之后会改变其中某些变量的值,比如++i就是一个副作用表达式)
char* fgets(char* str,int count,FILE* stream)
(1)函数作用:
从stream这个指针所指向的文件中获取count-1个字符存放到str的字符数组中,成功时返回str,失败返回空指针
int fputc(int ch,FILE* stream)
int putc(int ch,FILE* stream)
(1)函数作用:
将字符ch写到stream文件流中。
(2)返回值:
成功时返回被写入的字符
失败返回EOF并设置stream上的错误指示器
int fputs(const char* str,FILE* stream);
int fputs(const char* restrict str,FILE* restrict stream)
(1)函数作用:
将以NULL结尾的字符串str的每个字符写入到stream文件流中
(2)参数:
str:要写入的字符串
stream:输出流
(3)返回值:
成功时返回非负值,失败时返回EOF
(4)注意:
函数int puts(const char* str,FILE* stream)会将换行符附加到str字符串中输出给stream文件流中
而fputs写入的字符串并不会进行修改
int getchar(void)
(1)返回值:
成功时为获得的字符,失败时为EOF
char* gets_s(char* str,rsize_t n)
(1)函数说明:
从stdin里面读取n-1个字符存储到str中,不存在缓冲区,然后遇到换行或者符或者文件尾,就会去掉换行符,并附加空字符。
(2)返回值:
成功时返回str指针,失败返回空指针
(3)备注:
fgets()函数不进行边界检查,从而导致此函数对缓冲区溢出攻击极度脆弱,无法安全使用,所以被弃用。
int putchar(int ch);
(1)返回值:
成功时返回写入的字符,失败返回EOF并设置stdout上的错误指示器
(2)说明:
putchar()函数的参数ch必须是处于0-127之间的一个十进制整数
如果整型ch超出八位变量的范围时,ch则会被强转为8位变量(取低八位)
如果整型ch为负数的时候,由于计算机存储负数是补码的方式,所以会被当做正数处理,也是取低八位
(3)示例:
int main()
{
int res=0;
for(char c='a';(c!=EOF)&&(c!='z');++c)
{
res=putchar(c);
}
if(res==EOF)
{
if(ferror(stdout))
{
fprintf(stderr,"putchar() failed\n");
perror("putchar()");
exit(EXIT_FAILURE);
}
}
putchar("\n");
//验证超出八位的字符输出结果是否和参数一样——答案肯定是不一样的
int x=0x1070;
printf("\n0x%x\n",r);
x=putchar(x);
printf("\n0x%x\n",r);
}
int puts(const char* str)
(1)函数参数
str:要写入的字符串
(2)返回值
成功返回非负值,失败返回EOF
int ungetc(int ch,FILE* stream);
(1)函数说明:
将ch字符放到流stream的输入缓冲区,从而能够从stream的后继读取该字符,而不会修改与流关联的外部设备。
(2)参数
ch:要推入输入流缓冲区的字符
stream:要回放字符到文件流
(3)返回值:
成功返回ch,失败返回EOF
(4)说明:
①使用fseek\fsetpos\rewind将会忽略ungetc所加入的字符
②如果调用一次ungetc之后,不读取或者不重新寻位,然后再次调用ungetc就有可能调用失败,因为回放缓冲区大小可能位1)
③如果成功调用多次ungect,读取操作以ungect的逆序取得回访的字符
④如果ch=EOF,操作失败不会影响流
⑤实践中,回放缓冲区的大小会在4K和A或者保证的最小值1之间变化
(5)示例:
int main()
{
int res=0;
char ch;
printf("请输入整数:\n");
while((ch=getchar())!=EOF&&isdigit(ch))
{
res=res*10+ch-'0';
}
if(ch!=EOF)
{
ungect(ch,stdin);//将字符ch退回到输入流
}
printf("%d,uninteger--%c\n",res,(char)(getchar()));
return 0;
}
/*
请输入整数:
666x
//输出:
666 uninteger--x
*/