标准I/O
流的打开
fopen函数
下列函数可用于打开一个标准I/O流:
FILE *fopen(const char *path,const char *mode);
成功时返回流指针(大于0的值);出错时返回NULL
mode参数:
"r"或"rb" | 以只读方式打开文件,文件必须存在 |
---|---|
"r+"或"r+b" | 以读写方式打开文件,文件必须存在 |
"w"或"wb" | 以只写方式打开文件,若文件存在则文件长度清为0。若文件不存在则创建啊 |
"w+"或"w+b" | 以读写方式打开文件,其他同"w"。可读指的是文件清空后新写入的内容可读 |
"a"或"ab" | 以只写方式打开文件,若文件不存在则创建;向文件写入的数据被追加到文件末尾。 |
"a+"或"a+b" | 以读写方式打开文件。其他同"a"。 |
当给定"b"参数时,表示以二进制方式打开文件,但Linux下忽略该参数。
打开一个标准I/O流的六种不同的方式
限制 | r | w | a | r+ | w+ | a+ |
---|---|---|---|---|---|---|
文件必须已存在 | √ | √ | ||||
擦除文件以前的内容 | √ | √ | ||||
流可以读 | √ | √ | √ | √ | ||
流可以写 | √ | √ | √ | √ | √ | |
流只可在尾端处写 | √ | √ |
for example:
#include <stdio.h>
int main()
{
FILE *fp;
if((fp=open("test.txt","r+"))==NULL)
{
printf("fopen error\n");
return -1;
}
else
{
printf("fopen success\n");
}
return 0;
}
fopen
新建文件权限
fopen()
创建的文件默认访问权限是0666(rw-rw-rw-
),可实际最终的权限受``umask`影响- Linux系统中``umask
设定会影响文件的访问权限,其规则为(0666&~
umask),系统
umask`默认为0222,通过规则计算得到的最终权限是644 - 用户可以通过``umask`函数修改相关设定,修改只影响当前程序
- 如果希望``umask
不影响文件访问权限,该设定为0,即
umask(0);`
处理错误信息
extern int errno;
void perror(const char *s);
char *strerror(int errno);
errno
存放错误号perror
先输出字符串s,再输出错误号对应的错误信息strerror
根据错误号返回对应的错误信息
for example1:
#include <stdio.h>
int main()
{
FILE *fp;
if((fp=fopen("test.txt","r+"))==NULL)
{
perror("fopen");
return -1;
}
else
{
printf("fopen success\n");
}
return 0;
}
//output: fopen:No such file or directory
for example2:
#include <stdio.h>
#include <string.h>//strerror 头文件
#include <errno.h>//errno 头文件
int main()
{
FILE *fp;
if((fp=fopen("test.txt","r+"))==NULL)
{
printf("fopen:%s\n",strerror(errno));
return -1;
}
else
{
printf("fopen success\n");
}
return 0;
}
//output: fopen:No such file or directory
流的关闭
int fcloe(FILE *stream);
fclose()
调用成功返回0,失败返回EOF,并设置errno
- 流是有缓冲的,往流里面写入时,先写入流的缓冲区中,当缓冲区满了后才会写到实际的文件中
- 流关闭时自动刷新缓冲中的数据并释放缓冲区
- 当一个程序正常终止时,所有打开的流都会被关闭
- 流一旦关闭后就不能执行任何操作
程序中能够打开的文件或流的个数有限制,如何测试?
思路:循环打开流,成功则计算器累加,直到出错为止
答案:1021+stdin+stdout+stderr=1024
#include <stdio.h>
#include <string.h>
int main()
{
FILE *fp;
int cout=0;
while((fp=fopen("1.txt","w"))!=NULL)
{
cout++;
}
printf("cout:%d\n",cout);
return 0;
}
流的读写
读写一个字符:fgetc()/fputc()
一次读/写一个字符
读写一行:fgets()/fputs()
一次读/写一行
读写若干个对象:fread()/fwrite()
每次读/写若干个对象,而每个对象具有相同的长度
按字符输入
#include <stdio.h>
int fgetc(FILE *stream);
int getc(FILE *stream);
int getchar(void);
- 成功时返回读取的字符;若到文件末尾或出错时返回EOF
getchar()
等同于fgetc(stdin)
for example
#include <stdio.h>
int main()
{
int c;
c=fgetc(stdin);
printf("%c\n",c);
FILE *fp;
int ch,count=0;
if((fp=fopen("1.txt","r"))==NULL)
{
perror("fopen");
return 0;
}
while((ch=fgetc(fp))!=EOF)
{
count++;
}
printf("total %d bytes\n",count);
fclose(fp);
return 0;
}
按字符输出
#include <stdio.h>
int fputc(int c,FILE *stream);
int putc(int c,FILE *stream);
int putchar(int c);
- 成功时返回写入的字符;出错时返回EOF
putchar(c)
等同于fputc(c,stdout)
for example
#include <stdio.h>
int main()
{
fputc('a',stdout);
putchar('\n');
FILE *fp;
int ch;
if((fp=fopen("1.txt","w"))==NULL)
{
perror("fopen");
return 0;
}
for(ch='a';ch<='z';ch++)
{
fputc(ch,fp);
}
fclose(fp);
return 0;
}
exercise1:
如何利用fgetc
/fputc
实现文件的复制
#include <stdio.h>
int main()
{
FILE *fps,*fpd;
int ch;
if((fps=fopen("1.txt","r"))==NULL)
{
perror("fopen");
return 0;
}
if((fpd=fopen("2.txt","w"))==NULL)
{
perror("fopen");
return 0;
}
while((ch=fgetc(fps))!=EOF)
{
fputc(ch,fpd);
}
fclose(fps);
fclose(fpd);
return 0;
}
按行输入
#include <stdio.h>
char *gets(char *s);
char *fgets(char *s,int size,FILE *stream);
- 成功时返回s,到文件末尾或出错时返回NULL
- gets不推荐使用,容易造成缓冲区溢出,未限定输入的长度
- 遇到’\n’或已输入size-1个字符时返回自动添加结束符’\0’
for example
#include <stdio.h>
#define N 6
int main()
{
char buf[N];
fgets(buf,N,stdin);
printf("%s",buf);
return 0;
}
按行输出
#include <stdio.h>
int puts(const char *s);
int fputs(const char *s,FILE *stream);
- 成功时返回输出的字符个数;出错时返回EOF
- puts将缓冲区s中字符串输出到stdout,并追加’\n’
- fputs将缓冲区s中的字符串输出到stream
for example
#include <stdio.h>
int main()
{
puts("hello world");
FILE *fp;
char buf[]="hello world";
if((fp=fopen("1.txt","a"))==NULL)
{
perror("fopen");
return 0;
}
fputs(buf,fp);
fclose(fp);
return 0;
}
exercise2:
如何统计一个文本文件包含多少行?
如何判断读取了一行?
#include <stdio.h>
#include <string.h>
int main()
{
FILE *fp;
int line=0;
char buf[64];
if((fp=fopen("data.txt","r"))==NULL)
{
perror("fopen");
return -1;
}
while((fgets(buf,64,fp))!=NULL)
{
if(buf[strlen(buf)-1]=='\n')line++;
}
return 0;
}
按指定对象读写
#include <stdio.h>
size_t fread(void *ptr,size_t size,size_t n,FILE *fp);
size_t fwrite(const void *ptr,size_t size,size_t n,FILE *fp);
- 成功返回读写的对象个数;出错时返回EOF
for example
#include <stdio.h>
#include <iostream>
using namespace std;
struct student{
int no;
char name[8];
float score;
}s[]={{1,"hu",100},{2,"hui",98}};
int main()
{
FILE *fp;
if((fp=fopen("1.txt","w+"))==NULL)
{
perror("fopen");
return 0;
}
fwrite(s,sizeof(student),2,fp);
struct student tmp;
fread(&tmp,sizeof(student),2,fp);
cout<<tmp.name<<" "<<tmp.no<<" "<<tmp.score<<endl;
fclose(fp);
return 0;
}
小结
fgetc/fputc
操作文本文件或二进制数据文件读写,按字符读写,每次操作一个字符,效率低,fgets/fputs
按行读写,只能处理文本文件读写,二进制文件包含0,适合是字符串读写,对于0来说理解为字符串的终止符fread()/fwrite()
按照指定对象读写,文本文件和二进制文件都能处理,可以指定读写大小,效率高
exercise3:
如何利用fread/fwrite
实现文件的复制
#include <stdio.h>
#define N 64
int main()
{
FILE *fps,*fpd;
char buf[N];
int n;
if((fps=fopen("1.txt","r"))==NULL)
{
perror("fopen");
return 0;
}
if((fpd=fopen("2.txt","w"))==NULL)
{
perror("fopen");
return 0;
}
while((n=fread(buf,1,N,fps))>0)
{
printf("n=%d\n",n);
for(int i=0;i<n;i++)
{
printf("buf[%d]=%c\n",i,buf[i]);
}
fwrite(buf,1,n,fpd);
}
fclose(fps);
fclose(fpd);
return 0;
}
流的刷新
流自动刷新缓冲区的几种情形
- 流的缓冲区满了 ,流的缓冲区出现换行符时
- 全缓冲只有缓冲区满了才会刷新,行缓冲区遇到换行符或缓冲区满了都会刷新
- 流关闭时会把缓冲内的数据写到文件中
fflush
刷新流缓冲
#include <stdio.h>
int fflush(FILE *fp);
- 成功时返回0;出错时返回EOF
- 将流缓冲区中的数据写入实际的文件
- Linux下只能刷新输出缓冲区
for example
#include <stdio.h>
int main()
{
FILE *fp;
if((fp=fopen("1.txt",""))==NULL)
{
perror("fopen");
return 0;
}
fputc('a',fp);//a字符写到流的缓冲区里,实际没有写到文件中
fflush(fp);//刷新流的缓冲区 缓冲区的内容会写到实际的文件中
while(1);
return 0;
}
流的定位
#include <stdio.h>
long ftell(FILE *stream);
long fseek(FILE *stream,long offset,int whence);
void rewind(FILE *stream);
ftell()
成功时返回流的当前读写位置,出错时返回EOFfseek()
定位一个流,成功时返回0.出错时返回EOF- whence参数:SEEK_SET/SEEK_CUR/SEEK_END
- offset参数:偏移量,可正可负
rewind()
将流定位到文件开始位置- 读写流时,当前读写位置自动后移
示例一(在文件末尾追加字符’t’)
FILE *fp=fopen("1.txt","r+");
fseek(fp,0,SEEK_END);
fputc('t',fp);
示例二(获取文件长度)
FILE *fp=fopen("1.txt","r+");
fseek(fp,0,SEEK_END);
printf("length is %d\n",ftell(fp));
判断流释放出错和结束
#include <stdio.h>
int ferror(FILE *stream);
int feof(FILE *stream);
ferror()
返回1表示流出错;否则返回0feof()
返回1表示文件已到末尾;否则返回0
格式化输出
#include <stdio.h>
int printf(const char *fmt,...);
int fprintf(FILE *stream,const char *fmt,...);
int sprintf(char *s,const char *fmt,...);
- 成功时返回输出的字符个数;出错时返回EOF
for example
以指定格式"年-月-日"分别写入文件和缓冲区
#include <stdio.h>
int main()
{
int year,month,date;
FILE *fp;
char buf[64];
year=2020;
month=11;
date=22;
fp=fopen("1.txt","a+");
fprintf(fp,"%d-%d-%d\n",year,month,date);
sprintf(buf,"%d-%d-%d\n",year,month,date);
printf("buf:%s",buf);
return 0;
}
exercise
- 每隔1秒向文件data.txt写入当前系统时间,格式如下:
1, 2014-10-15 15:16:42
- 该程序无限循环,直到按Ctrl+C中断程序
- 每次执行程序时,系统时间追加到文件末尾,序号递增
- time()用来获取系统时间(秒数)
- localtime()将系统时间转换成本地时间
- sleep()实现程序睡眠
- 以何种方式打开流?
- 流的缓冲类型对文件写入的影响
#include <stdio.h>
#include <unistd.h>//sleep
#include <time.h>// time localtime
#include <string.h>
int main()
{
FILE *fp;
int line=0;
char buf[64];
time_t t;
struct tm *tp;
if((fp=fopen("data.txt","a+"))==NULL)
{
perror("fopen");
return -1;
}
while((fgets(buf,64,fp))!=NULL)
{
if(buf[strlen(buf)-1]=='\n')line++;
}
while (1)
{
time(&t);
tp=localtime(&t);
fprintf(fp,"%02d, %d-%02d-%02d %02d:%02d:%02d\n",++line,tp->tm_year+1900,tp->tm_mon+1,tp->tm_mday,tp->tm_hour,tp->tm_min,tp->tm_sec);
fflush(fp);
sleep(1);
}
return 0;
}