目录
一、流和FILE对象
对于标准I/O库,对他们的操作是围绕流(stream)进行的,用标准I/O库打开或者创建一个文件时,已使一个流与一个文件相结合。当打开一个流时,标准I/O函数fopen返回一个指向FILE的指针,该对象通常是一个结构体,包含了I/O库为管理该流所需要的所有信息,包括文件描述符,指向流缓存的指针,缓存长度,当前缓存的字节数,出错标志等。指向FILE的指针被称为文件指针。
基于流的I/O操作过程大致可以归纳如下:通过调用fopen函数打开,并且返回一个FILE指针,成功打开流之后,就可以调用库函数对其进行I/O操作,操作完成后,需要执行清空缓冲区,保存数据等操作,然后关闭流。
当使用流I/O时,标准输入,标准输出,标准出错这3个流在进程启动时是默认打开,在基于流的I/O中,通过预定义文件指针stdin,stdout,stderr来引用标准输入,标准输入和标准出错。
基于流的操作最终会调用write和read函数进行I/O操作,为减少多次调用,流对象通常会提供缓冲区以减少调用次数。标准I/O提供了3种类型的缓存:
全缓存:直到缓冲区被填满,才调用I/O函数。
行缓存:直到遇到换行符'\n',才调用I/O函数,stdin,stdout。
无缓存:数据立即读入或者输出,stderr。
二、基于流的I/O操作
1.对缓存的操作
当调用fopen函数打开一个流时,就开辟了缓存区,缓存区参数有系统默认的值,实际使用时,可以根据自己的需求来进行更改:
void setbuf(FILE* fp,char* buf);
void setbuffer(FILE* fp,char* buf,size_t size);
void setlinebuf(FILE* fp);
void setvbuf(FILE* fp,char* buf,int mode,size_t size);
前3个函数没有返回值,setvbuf函数成功返回0,出错返回非0,fp是FILE结构体指针,应指向一个打开的流。
setbuf函数将缓冲区设置为全缓存或者无缓存,当buf指向一个真实的缓冲区地址时,表示全缓存,当buf指向NULL时,表示无缓存;
setbuffer同setbuf类似,size参数用来指定缓冲区大小;
setlinebuf专用于设置为行缓存;
setvbuf中参数mode用来指定缓冲区类型(_IOFBF(全缓存),_IOLBF(行缓存),_IONBF(无缓存))。
如下,修改stdin的缓存:
#include <stdio.h>
#include <stdlib.h>
#define SIZE 1024
int main()
{
char buf[SIZE];
//unbuffered
if(setvbuf(stdin,buf,_IONBF,SIZE)!=0)
{
printf("some error\r\n");
exit(1);
}
else
{
printf("Set successful\r\n");
printf("stdin is:\r\n");
// distinguish the types of buf
if(stdin->_flags & _IONBF)
printf("unbuffered\r\n");
else if(stdin->_flags & _IOLBF)
printf("line-buffered\r\n");
else
printf("full-buffered\r\n");
printf("buffer size is %ld\n",stdin->_IO_buf_end-stdin->_IO_buf_base);
}
// full-buffered
if(setvbuf(stdin,buf,_IOFBF,SIZE)!=0)
{
printf("some error\r\n");
exit(1);
}
else
{
printf("Change successful\r\n");
printf("stdin is:\r\n");
// distinguish the types of buf
if(stdin->_flags & _IONBF)
printf("unbuffered\r\n");
else if(stdin->_flags & _IOLBF)
printf("line-buffered\r\n");
else
printf("full-buffered\r\n");
printf("buffer size is %ld\n",stdin->_IO_buf_end-stdin->_IO_buf_base);
}
return 0;
}
执行结果如下:
缓存的清洗;
int fflush(FILE *fp) ;
该函数用于将缓冲区尚未写入的文件强制性的保存在文件中,调用成功返回0,失败返回EOF。
2,流的打开与关闭
流的打开:
对一个流进行操作之前,首先要打开流,即建立某个流与特定文件和设备之间的关联,打开流的函数如下:
FILE * fopen(const char* pathname,const char* type);
FILE * freopen(const char* pathname,const char* type,FILE *fp);
FILE* fdopen(int fd,const char* type);
fopen函数打开路径为pathname的文件;
freopen函数在一个特定的流上,打开指定文件,若已经打开,则先关闭,一般用于将一个指定文件打开为一个预定义的流:标准输入,标准输出,标准出错;
fdopen取一个现成的文件描述符,并使得一个标准I/O流与该文件描述符相结合。
type参数指定了对I/O的读写方式,常见的有:r,w,w+,a,a+,rb等等,a表示追加写,b表示以二进制文件打开。
流的关闭:
int fclose(FILE *fp);成功返回0,否则返回-1。
fclose函数在关闭文件时,会将存在于缓冲区未来得及写进磁盘的内容写进磁盘。
例如,打开和关闭一个流:stream.c
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
int main()
{
FILE *fp; //creat a ptr pointed to file stream
int fd;
//using fopen to open by stream
if((fp=fopen("/home/huahua/Linux_C/file/stream.txt","r+"))==NULL)
{
printf("fail to open1\r\n");
exit(1);
}
fprintf(fp,"This is Linux C!\n");
fclose(fp);
//打开文件,获取文件描述符
if((fd=open("/home/huahua/Linux_C/file/stream.txt",O_RDWR))==-1)
{
printf("fail to open2\r\n");
exit(1);
}
//using fdopen to open by stream
if((fp=fdopen(fd,"a+"))==NULL)
{
printf("fail to open stream");
exit(1);
}
fprintf(fp,"I am doing a Linux program!\n");
// printf("debug------1\n");
fclose(fp); //cloae this stream
return 0;
}
3,流的读写
一旦打开了流,可在四种不同类型的I/O中进行选择来进行读写操作:
基于字符的I/O:
用来处理单个字符。
读入字符: int getc(FILE*fp);
int fgetc(FILE* fp);
int getchar(void);
成功返回读取的字符,到达文件尾或者出错返回EOF,fp表示要读入字符的文件,getchar用于标准读入,相当于fget(stdin);区分发生错误或者到达文件尾使用ferror和feof。
输出字符: int putc(int c,FILE *fp);
int fputc(int c,FILE *fp);
int putchar(int c);
成功返回c,出错返回EOF,putchar用于标准输出;
基于行的I/O:
当内容遇到'\n'时,将流中'\n'之前的内容送到缓冲区。
行的读入:
char * fgets(char* buf,int n,FILE *fp);
char * gets(char *buf);
成功返回缓冲区的首地址,到达文件尾或者出错返回NULL,buf表示读入串的缓冲区,n表示读入字符的个数,n不大于缓冲区的长度,gets函数用于标准输入。
行的输出:
int fputs(const char *buf,FILE *restrict fp);
int puts(const char *str);
成功返回输出的字节数,失败返回-1,buf 表示存放输出内容的缓冲区,fp表示要输出的文件。
直接I/O:
size_t fread(void * ptr,size_t size ,size_t nmemb,FILE *fp);
size_t fwrite(const void * ptr,size_t size ,size_t nmemb,FILE *fp);
fread用于输出操作,ptr是指向读取数据缓冲区的指针,size是读取对象的大小,nmemb是读取对象的个数;
fwrite用于输入操作,参数类似fread。
均返回读或写得对象数。
如下,实现文件复制操作:
#include <stdio.h>
#include <stdlib.h>
#define Src_File "/home/huahua/Linux_C/file/scr.txt"
#define Des_File "/home/huahua/Linux_C/file/des.txt"
int main()
{
FILE *fp1,*fp2;
char buf[1024];
int n;
if((fp1=fopen(Src_File,"rb"))==NULL)
{
printf("scr file error\n");
exit(1);
}
if((fp2=fopen(Des_File,"wb"))==NULL)
{
printf("des file error\n");
exit(1);
}
while((n=fread(buf,sizeof(char),1024,fp1))>0)
{
if(fwrite(buf,sizeof(char),n,fp2)==-1)
{
printf("fail to write\n");
exit(1);
}
if(n==-1)
{
printf("fail to read\n");
exit(1);
}
}
fclose(fp1);
fclose(fp2);
return 0;
}
运行结果scr.txt文件内容成功复制到des.tx中。
格式化I/O:
int printf(const char* format,...);
int fprintf(FILE *fp,const char *format);
成功返回输出的字符数,出错返回负值,printf用于标准输i
int scanf(const char* format,...);
int fscanf(FILE *fp,const char* format,...);
成功返回输入项数,出错或者到结尾返回EOF,scanf从标准流输入,fsanf从指定流输入。
如下程序,实现输入输出:
#include <stdio.h>
#define File_path "/home/huahua/Linux_C/file/scr.txt"
int main()
{
FILE *fp;
char buf[]="What a hot day!!!\n";
char buf2[80];
fp=fopen(File_path,"w");
fprintf(fp,"%s",buf);
fprintf(fp,"\n");
fclose(fp);
fp=fopen(File_path,"r");
fscanf(fp,"%s",buf2);
fclose(fp);
printf("%s\n",buf2);
return 0;
}