流和FILE对象
文件IO的所有操作都是基于文件描述符fd,而标准IO的所有操作都是围绕流进行的。当用标准I/O库打开或创建一个文件时,我们使一个流和一个文件关联
标准输入,标准输出,标准出错
对每个进程预定于流三个流,他们是STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO
缓冲
标准I/O库提供缓冲对目的是尽可能对减少使用read/write系统调用的次数,它对每个I/O流自动地进行缓冲管理。
标准I/O提供三种形式的流管理
- 全缓冲
在填满缓冲区后才进行I/O,可以手动调用fflush
手动缓冲, - 行缓冲
当I/O 中携带遇到换行符是,才进行I/O操作,这允许我们一次写入一个字符。当流涉及终端时,通常使用行缓冲。行缓冲的长度通常是固定的,当超过缓冲区长度后,也会进行I/O操作。任何时候只要通过标准I/O库要求从一个1)不带缓冲的流,或2)行缓冲的流得到输入数据,都将冲洗所有行缓冲输出流。 - 不缓冲
不对字符进行缓冲存储,标准错误通常不缓冲。
更改流的缓冲类型
#include<stdio.h>
void setbuf(FILE *restrict fp, char *restrict buf);
int setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);
#成功返回0,出错返回非0值
#关闭缓冲,将buf设置为NULL,
#setvbuf,可以精确制定缓冲类型,使用mode=(_IOFBF,_IOLBF,_IONBF),如果不缓冲,忽略buf和size参数,如果制定行缓冲和全缓冲,需要制定相应的缓冲区以及长度
int fflush(FILE *fp) #冲洗缓冲区
打开流
#include <stdio.h>
FILE *fopen(const char *restrict pathname, const char *restrict type)
#打开一个指定文件
FILE freopen(const char *restrict pathname,const char *restrict type, FILE *restrict fp)
#在指定流fp上打开指定文件,若该流已打开,则先关闭该流。。若该流已经重定向,则清楚该定向。
FILE fdopen(int fileds, const char *type)
#获取一个现有文件描述符,,并是一个标准I/O流与该文件描述符结合.常用于由创建管道和网络通信通道函数返回的描述符。
#成功返回文件指针,出错返回NULL
int fclose(FILE *fp)
#成功返回0,出错返回EOF
type
通常指 r,w,a...
当以读写类型(r+,w+)打开文件时,具有下列限制
- 如果中间没有fflush,fseek,fsetpos,rewind,则在输出后不能直接跟随输入
- 如果中间没有fseek,fsetpos或rewind,或者一个输入操作没有到达文件尾端,则在输入操作后不能直接跟随输出。
在文件关闭之前,会冲洗缓冲区的所有数据,丢弃缓冲区中任何输入数据,如果标准I/O为该流自动分配了一个缓冲区,则释放此缓冲区。
当一个进程正常正常终止时,则所有未写缓冲数据的标准I/O流都会被冲洗,所有打开的I/O流都会被关闭。
读写一个字符
#include<stdio.h>
int getc(File *fp)
int fgetc(File *fp)
int getchar(void) = getc(stdin) #从标准输入读
#若成功,则返回下一个字符,出错或达到文件尾端返回EOF
#未区分出错或到达文件尾端,可以使用如下函数
int ferror(FILE *fp)
int feof(FILE *fp)
#条件为真返回非0,否则返回0
void clearerr(FILE *fp)
每个流在FILE中维护两个标志
- 出错标志
- 文件结束标志
调用clearerr
可以清楚这两个标志。
从流中读取数据后,可以使用ungetc
将字符压回流中
#include<stdio.h>
int ungetc(int c, FILE *fp)
#成功返回c,则出错返回EOF
# 压送回的流可以被再次读出,但读出字符的顺序与压送回的顺序相反。
#ungetc会清除该流的文件结束标记
每次一行I/O
#include<stdio.h>
char fgets(char *restrict buf, int n, FILE *fp)
#必须指定缓冲区的长度n,如果没有读到换行符,则读到n-1个字节为止。如果该行长度超过n-1,则fgets会返回不完整的行
char *gets(char *buf) #从标准输入读
#gets不推荐使用,因为无法指定缓冲区长度,如果写入的内容大于缓冲区长度,会造成缓冲区溢出,写到缓冲区后面的空间中。
#成功返回buf,出错到文件末尾返回NULL
#include<stdio.h>
int fputs(const char* restrict buf, FILE *restrict fp)
#以null终止的字符串写入到指定的流,尾端终止符null不写出。
int puts(const char *str)
# 将以null符终止的字符串写到stdout,null终止符不写出,但是puts又将一个换行符写到stdout
#成功返回非负值,失败返回EOF
疑点:如何处理换行符
尽量使用fgets,fputs
标准I/O的效率
二进制I/O
#include<stdio.h>
size_t fread(void *restrict ptt, size_t size, size_t nobj, FILE *restrict fp)
size_t fwrite(const void *restrict ptr,size_t size, size_t nobj,FILE *restrict fp)
#返回读或写的对象数
使用二进制io的时候,只能用于读在同一系统已写过的数据,无法对异构系统数据进行读写。
定位流
long ftell(FILE *fp)
#返回值:若成功则返回当前未见位置指示,出错返回-1L
int fseek(FILE *fp, long offset, int whence)
# 返回,若成功返回0,出错返回非0
void rewind(FILE *fp)
格式化I/O
#include<stdio.h>
int printf(const char *restrict format,...);
int fprintf(FILE *restrict fp, const char *restirct format ,...);
#写到指定的流
#成功返回输出字符数,出错返回负值
int sprintf(char *restrict buf, const char *restrict format,...);
#将格式化字符写送入数组buf中,该函数存在buf指向的缓冲区溢出的风险,调用者有责任确保该缓冲区足够大。
int snprintf(char8restirct buf, size_t n, const char *restrict format,...)
#相较于上面的sprintf,该函数指定了缓冲区长度,如果超过长度的数据,会被丢弃。
#成功返回存入数组的字符数,出错返回负值
总结:
标准io通过对文件i/o的封装,来进行输入输出操作,在操作对象上,标准io操作的对象是流,文件io操作的是文件描述符。
文件io每次read,write都调用内核中的系统调用,而标准io则通过缓冲区减少系统调用的次数。
网友评论