只要操作系统中安装了C库就可以调用标准I/O的函数
标准I/O可以减少系统调用的次数,在执行系统调用的时候系统必须从用户态切换到内核态,处理相应请求再返回用户态,频繁切换会增加系统的开销,标准I/O使用时再用户空间创建了一个缓存区,读写时,先操作缓冲区,在合适的时机再通过系统调用访问实际的文件,从而减少了使用系统调用的此时。
注意:因为我们是以文件为例,所以我们把后面设计到的流都称为文件。
打开文件:FILE* fopen(const char *path,const char *mode)
头文件:#include<stdio.h>
返回值:成功:返回一个文件类型的指针,失败:返回NULL
第一个参数:一个指向字符串常量的指针,或者说是一个字符串
第二个参数:文件打开的方式,如a,b等详细描述见下表,也是一个字符串
r或rb | 只读,文件必须存在 |
---|---|
r+或r+b | 可读写,文件必须存在 |
w或wb | 只写,清空原有内容,文件不存在就建立 |
w+或w+b | 可读写,清空原文件,文件不存在就建立 |
a或ab | 只写,新写的数据加到文件末尾,文件不存在就建立 |
a+或a+b | 可读写,新写的数据加到文件末尾,文件不存在就建立 |
关闭文件:int fclose(FILE *Stream)
头文件:#include<stdio.h>
返回值:成功:返回0,失败返回:EOF(-1)
参数:一个文件类型的指针
实例:
#include<stdio.h>
int main()
{
FILE *fp;
if((fp=fopen("路径/文件名","r"))==NULL)
{
perror("open fail");//打印错误信息
return -1;
}
fclose(fp);
}
按字节输入:int getc(FILE *Stream)
按字节输入:int fgetc(FILE *Stream)
头文件:#include<stdio.h>
返回值:成功:读取的字符,失败:返回EOF(-1)
参数:文件类型的指针
例子:
#include<stdio.h>
int main()
{
FILE *fp;
if((fp=fopen("路径/文件名","r"))==NULL)
{
perror("open fail");//打印错误信息
return -1;
}
char ch=fgetc(fp);
printf("%c\n",ch);//查看获取的字符
fclose(fp);
}
按字节输出:int putc(int c,FILE *Stream)
按字节输出:int fputc(int c,FILE *Stream)
头文件:#include<stdio.h>
返回值:成功:输出的字符,失败:返回EOF(-1)
参数:文件类型的指针
例子:
#include<stdio.h>
int main()
{
FILE *fp;
if((fp=fopen("路径/文件名","r"))==NULL)
{
perror("open fail");//打印错误信息
return -1;
}
char ch='1';
fputc(ch,fp);
fclose(fp);
}
按行输入char* fgets(char *s,int size,FILE* Stream)
头文件:#include<stdio.h>
返回值::成功:返回s,失败或到达文件末尾:返回:NULL;
第一个参数:存放输入字符串缓冲区的首地址
第二个参数:输入字符串的大小(缓冲区大小应该-1>=size,否则容易出问题)
第三个参数:一个指向文件的指针
例子:
#include<stdio.h>
#define SIZE 5
int main()
{
FILE *fp;
if((fp=fopen("路径/文件名","r"))==NULL)
{
perror("open fail");//打印错误信息
return -1;
}
char ch[SIZE];
fgets(ch,SIZE,fp);
fclose(fp);
}
按行输出:int fputs(const char *s,FILE* Stream)
头文件:#include<stdio.h>
返回值::成功:返回s,失败:返回:NULL;
第一个参数:存放输出字符串的首地址
第二个参数:一个指向文件的指针
例子:
例子:
#include<stdio.h>
#define SIZE 5
int main()
{
FILE *fp;
if((fp=fopen("路径/文件名","r"))==NULL)
{
perror("open fail");//打印错误信息
return -1;
}
char ch[10]="hello";
fputs(ch,fp);
fclose(fp);
}
在文件中定位到指定位置读写:int fseek(FILE *stream,long offset,int whence);
头文件:#include<stdio.h>
返回值:成功返回0,失败返回EOF(-1);
第一个参数:一个指向文件的指针
第二个参数:相对于基准位置的偏移量
第三个参数:基准值
whence | 基准值 |
---|---|
SEEK_SET | 代表文件起始位置 |
SEEK_END | 代表文件结束位置 |
SEEK_CUR | 代表文件当前读写位置 |
返回当前文件的位置:long ftell(FILE *stream);
头文件:#include<stdio.h>
返回值:成功:返回当前读写位置,失败:返回EOD(-1)
参数:一个指向文件的指针
以指定大小为单位读文件:
size_t fread(void *ptr,size_t size,size_t nmemb,FILE* Stream)
头文件:#include<stdio.h>
返回值:成功返回实际读取到的记录个数; 失败:返回EOF(-1)
第一个参数:存放读入记录缓冲区的首地址
第二个参数:每次读取的记录的大小
第三个参数:读取的记录数
第四个参数:一个指向文件的指针
注意事项:一般应该满足:缓冲区的大小=第二个参数*第三个参数
以指定大小为单位写文件:
size_t fwrite(void *ptr,size_t size,size_t nmemb,FILE* Stream)
头文件:#include<stdio.h>
返回值:成功返回实际读取到的记录个数; 失败:返回EOF(-1)
第一个参数:存放写入记录缓冲区的首地址
第二个参数:每次写入的记录的大小
第三个参数:写入的记录数
第四个参数:一个指向文件的指针
下面给出一个文件复制的例子:
#include<stdio.h>
#define N 1024 //缓冲区的大小
#define n 1//预设记录数
#define m 1024//每个记录的大小
int file_size(const char* filename);
int copy_file(const char* s_f_name,const char* d_f_name);
int main()
{
copy_file("路径/源文件名","路径/目标文件名")
}
int file_size(const char* filename)
{
FILE *fp;
int SIZE=0;
if(NULL==(fp=fopen(filename,"r"))
{
perror("open fail");
return -1;
}
if(EOF==(fseek(fp,0.SEEK_END))//将读写位置定位到最末尾
{
perror("fseek fail");
return -1;
}
if(EOF!=SIZE=ftell(fp))//返回在当前文件中的位置
{
flcose(fp);
return SIZE;
}
else
{
flcose(fp);
return -1;
}
}
int copy_file(const char* s_f_name,const char* d_f_name)
{
FILE *fps,*fpd;
char buf[N];
long long S_SIZE=0;
int M=0;//实际的记录数
if(EOF==(S_SIZE=file_size(s_f_name)))
{
perror("file_size fail");
return -1;
}
if(NULL==(fps=fopen(filename,"r"))
{
perror("openfile");
}
if(NULL==(fps=fopen(filename,"w"))
{
perror("openfile");
}
if(S_SIZE>1024)
{
while(M=fread(buf,m,n,fps))//返回值M是实际读取到的记录数,当没能读取到记录数的时候退出循环
{ //这里的m*n应该小于等于缓冲区的大小,否则会出现段错误
fwrite(buf,m,M,fpd);//这里的M应该设计为实际的记录数
} //fwrite和fread每次读取记录应该相同大小,所以我都设置为m
}
else
{
while(M=fread(buf,S_SIZE,n,fps))
{
fwrite(buf,S_SIZE,M,fpd);
}
}
}
格式化输入int sscanf(char* buf,const char *format,...)
头文件:#include<stdio.h>
返回值:成功:返回输出字符数(从buf取出的字符数)。失败:返回EOF(-1)
第一个参数:作为输入的缓存区
第二个参数:输入格式
第三个参数及其以后的参数:用来接收转化后的字符
例子:
#include<stdio.h>
int main()
{
char buf1[10]="1234 123,23;
char buf2[20];
sscanf(buf1,"%d %d,%d",buf2,&buf2[1],&buf2[2]);
int i=0;
while(i<3)
{
printf("%d\n",buf2[i]);
}
}
格式化输入:int fscanf(FILE* buf,const char *format,...)
头文件:#include<stdio.h>
返回值:成功:返回输出字符数(从buf取出的字符数)。失败:返回EOF(-1)
第一个参数:作为输入的缓存区
第二个参数:输入格式
第三个参数及其以后的参数:用来接收转化后的字符
#include<stdio.h>
int main()
{
FILE *fp;
if(NULL==(fp=fopen("文件路径/文件名","r")))
{
perror("oepn fail");
return -1;
}
char buf2[20];
sscanf(fp,"%d %d,%d",buf2,&buf2[1],&buf2[2]);
int i=0;
while(i<3)
{
printf("%d\n",buf[i]);
}
fclose(fp);
}
格式化输出:int sprintf(char *buf,const char* format,...)
头文件:#include<stdio.h>
返回值:成功:返回输出字符数(输出到第1个参数中字符数)。失败:返回EOF(-1)
第一个参数:作为输出的缓存区
第二个参数:输出格式
第三个参数及其以后的参数:存放需要转化的数据
#include<stdio.h>
int main()
{
char buf1[10]={1,2,3};
char buf2[20];
sprintf(buf2,"%d%d%d",buf1[0],&buf1[1],&buf1[2]);
int i=0;
while(i<3)
{
printf("%c\n",buf1[i]);
}
}
打印结果:1
2
3
格式化输出:int fprintf(FILE *buf,const char* format,...)
头文件:#include<stdio.h>
返回值:成功:返回输出字符数(输出到第1个参数中字符数)。失败:返回EOF(-1)
第一个参数:作为输出的缓存区
第二个参数:输出格式,第三个参数是什么类型,第二个参数就要什么类型
第三个参数及其以后的参数:存放需要转化的数据
#include<stdio.h>
int main()
{
FILE *fp;
if(NULL==(fp=fopen("文件路径/文件名","a")))
{
perror("oepn fail");
return -1;
}
char buf1[10]={1,2,3};
sprintf(fp,"%d%d%d",buf1[0],&buf1[1],&buf1[2]);
}
执行成功后,指定的文件会在末尾添加buf1[0],buf1[1],buf1[2]中存放的数据。
一个显示时间的案例
#includ<stdio.h>
#include<unistd.h>//sleep的头文件
#include<time.h>//time相关头文件
int main(int argc,char *argv[])
{
FILE *fp;
time_t t;
if(argc<2)
{
printf("usage:%s<filename>",argv[0]);
}
if(NULL==(fp=fopen(argv[1],"w")))
{
perror("oepn fail");
return -1;
}
while(1)
{
time(&t);
fprintf(fp,"%s",ctime(&t))//注意默认是按行缓存的
fflush(fp);//强制清空fp流的缓冲区
//setbuf(fp,NULL);//fp流不设置缓冲区
sleep(1);
}
}
如果不设置缓冲区,缓冲区满了才会将数据放入我们指定的文件;