标准I/O
Buffered I/O:
Buffered I/O可以减少系统调用的次数,提高性能。每次读一个block的整数倍可以提高效率。
1、Standard I/O:
C语言的标准IO库stdio,提供了跨平台的,user-buffering的方案。
2、文件指针:
标准I/O不直接操作文件描述符,而是使用文件指针来操作文件。
FILE *fp
打开的文件被称为流,输入流、输出流、输入/输出流
3、打开文件:
#include <stdio.h>
FILE *fopen(const char *path, const char *mode);
mode描述了如何打开由path代表的文件:
r:打开文件,可读,流的位置在文件的开头
r+:打开文件,可读写,流的位置在文件的开头
w: 打开文件,可写,如果存在,清空。不存在创建,流的位置在文件的开头
w+:打开文件,可读写,如果存在,则清空,如果不存在则创建,流的位置在文件的开头
a:append模式,可写,如果不存在创建。流的位置在文件的末尾。
a+:append模式,可读写,如果不存在创建。流的位置在文件的末尾。
例子:
File *fp;
fp = fopen("/etc/manifest","r");
if(! fp )
/* error */
4、通过文件 描述符打开:
#include <stdio.h>
FILE *fdopen(int fd, const char *mode);
mode的含义和fopen一样,除了w、w+不会清空文件。
例子:
FILE *fp;
int fd;
fd = open("/home/kidd/map.txt",O_RDONLY);
if(fd == -1)
/* error */
fp = fdopen(fd,"r");
if(!fp)
/* error */
一个可读的fd,如果fdopen设置mode是w,会如何?二者不一致会返回NULL,并设置
errno为EIVAL.
5、关闭流:
fclose可以关闭文件。
#include <stdio.h>
int fclose(FILE *fp);
buffer和没有写的数据首先被fushed。成功返回0,失败返回EOF。
6、关闭所有的流:
fcloseall关闭当前进程所有的流:
#define _GNU_SOURCE
#include<stdio.h>
int fcloseall(void);
总是返回0。
7、从流中读:
1)一次读一个字符:
fgetc:
#include <stdio.h>
int fgetc(FILE *fp);
返回读取字符转换成了int值,主要由于考虑文件结尾返回EOF,-1不在assci码范围。
int c;
while((c = fgetc(fp) != EOF){
printf("%c\n",(char)c);
}
2)将字符压回流:
ungetc:
通过这个函数可以peek流中的字符,如果读超过了需要的字符,可以把
这个字符压回。
#include <stdio.h>
int ungetc(int c, FILE *fp);
成功返回c,失败返回EOF
3)读取一行:
fgets可以读取一行字符串:
#include <stdio.h>
char * fgets(char *str,int size, FILE *stream);
读取遇到换行或者EOF,成功返回str,失败返回NULL。
例子:
char buf[LINE_MAX];//LINE_MAX in <limits.h>du
if(!fgets(buf,LINE_MAX,fp)
/* error */
4)读二进制数:
fread:
#include <stdio.h>
size_t fread(void *buf, size_t size, size_t nr, FILE *fp);
可以读取复杂的二进制数,比如c中的结构体。
读nr个数据,每一个大小为size字节。成功返回读取元素的个数,失败指示错误或者返回EOF,
但是如果不使用ferror()和feof()无法区分。
由于在不同机器的变量大小,对其,补齐,字节序不同,不具有跨机器性。
例子:
char buf[64];
size_t nr;
nr = fread(buf,sizeof(buf),1,fp);
if(nr == 0)
/* error */
8、写入流:
1)写入一个字符:
#include <stdio.h>
int fputc(int c, FILE *fp);
成功返回c,失败返回EOF。
if( fputc('p',fp) )
/* error */
2)写入一个字符串:
fputs:
#include <stdio.h>
int fputs(const char *str, FILE *stream);
2)写入由\0结尾的字符串,成功返回非负数,失败返回EOF。
例子:
FILE *fp;
if(!(fp = fopen("journal.txt","a"))
/* error */
if(fputs("The ship is made of wood.\n",fp) == EOF)
/*error*/
if(fclose(fp) == EOF)
/*error*/
3)写入二进制数据:
fwrtie可以将复杂的结构数据存储到文件中:
#include <stdio.h>
size_t fwrite(void *buf,size_t size, size_t nr, FILE *fp);
将右buf指向的数据写入文件fp中nr个元素,每一个元素的长度是size字节。
9、使用Buffered I/O 例子:
#include <stdio.h>
int main(void){
FILE *in,*out;
struct pirate{
char name[100];
unsigned long booty;
unsigned int beard_len;
}p,blackbeard = {"Edward Teach",950,48};
out = fopen("data","w");
if(! out ){
perror("fopen");
return 1;
}
if(!fwrite(&blackbeard,sizeof(struct pirate),1,out)){
perror("fwrite");
return 1;
}
if(fclose(out)){
perror("fclose");
return 1;
}
in = fopen("data","r");
if( !in ){
perror("fopen");
return 1;
}
if(! fread(&p, sizeof(struct pirate),1,in)){
perror("fread");
return 1;
}
if(fclose(in)){
perror("fclose");
return 1;
}
printf("name=\"%s\" booty=%lu beard_len=%u\n",p.name,p.booty,p.beard_len);
return 0;
}
10、Seeking a Stream
1)fseek和标准IO lseek类似:
#include <stdio.h>
int fseek(FILE *fp,long offset, int whence);
whence可以取:SEEK_SET、SEEK_CUR,SEEK_END,和lseek含义一样。
2)fsetpos:
#include <stdio.h>
int fsetpos(FILE *fp,fpos_t *pos);
和fseek将whence设置成SEEK_SET一样。
3)rewind:
将文件位置重置,相当于fseek(fp,0,SEEK_SET);
#include <stdio.h>
void rewind(FILE *fp);
rewind 没有返回值,可以通过errno来判断是否成功:
errno = 0;
rewind(stream);
if(errno)
/* error */
11、获取当前文件流的位置:
1)ftell:
#include <stdio.h>
long ftell(FILE *stream);
成功返回位置,失败返回-1,errno被设置。
2)fgetpos:
#include <stdio.h>
int fgetpos(FILE *stream, fpos_t *pos);
成功返回0,并设置pos,失败返回-1。
12、flush a Stream:
fflush:
#include <stdio.h>
int fflush(FILE *fp);
所有由fp标示的未写入的数据写入内核的buffer中。
成功返回0,失败返回EOF,并设置errno
write都会写入用户的buffer中,fflush可以将用户buffer的数据,写入内核buffer中,
并不能保证写入磁盘。
13、错误和文件结尾:
一些标准的I/O比如fread,没有提供区分error和EOF的机制,所以检查所给的流确定遇到错误还是文件
结尾很有用。标准I/O提供了两个接口:
1)ferror:
#include <stdio.h>
int ferror(FILE *fp);
返回非0表示设置了错误,0表示没有。
2)feof
#include <stdio.h>
int feof(FILE *fp);
测试流是否设置了EOF,返回非零表示设置,0表示没有。
3)清除错误和EOF设置:
#include<stdio.h>
void clearerr(FILE *fp);
例子:
if(ferror(f))
printf("Error on f!\n");
if(feof(f))
printf("EOF on f!\n");
clearerr(f);
14、获得相关的文件描述符:
#include <stdio.h>
int fileno(FILE *fp);
成功返回相关的文件描述符,失败返回-1
混用标准I/O和IO系统调用并不是值得推荐的。
15、控制buffer:
a、标准I/O实现了三种用户Buffer:
1)Unbuffered:
没有缓存,数据直接交给内核。stderr默认使用这种方式。
2)Line-buffered:
buffer缓存一行,当遇到换行符则提交给内核。stdout默认使用这种方式。
3)Block-buffered
与文件相关的I/O默认都是Block-buffered。
b、设置流的buffer类型:
setvbuf:
#include <stdio.h>
int setvbuf(FILE *stream, char *buf, int mode, size_t size);
mode:
_IONBF: 没有buffer
_IOLBF: Line-buffered
_IOFBF: block-buffered/full buffered
除了_IONBF外,其他的两个,都需要用户提供一个size自己的buf。
提供的buffer在文件关闭的时候仍然要可见,否则显示的关闭操作会失败。
下面例子存在bug:
#include <stdio.h>
int main(void){
char buf[BUFSIZ];
setvbuf(stdout,buf,_IOFBF,BUFSIZ);
printf("Arrr!\n");
return 0;
}
程序退出的时候会隐式的关闭文件,导致失败。可以在上面程序return之前加上显示的close关闭stdout,或者
使用全局的buf。
16、格式化I/O:
1)使用可变参数族:
#include <stdio.h>
//正确返回输出的字节数,错误返回-1
int printf(const char *restrict format, ...);
int fprintf(FILE *restrict fp, const char * restrict format,...);
//调用者需要确保buf足够大,不会溢出,
int sprintf(char *restrict buf,const char * restrict format,...);
//显示指定了buf的大小,多出来的会被抛弃
int snprintf(char *restrict buf, size_t n, const char *restrict format,...);
//正确返回输出到数组buf中的字节数,错误返回-1。
2)使用va_list族:
va_list在<stdarg.h>中定义。
#include <stdio.h>
#include <stdarg.h>
int vprintf(const char *restrict format, va_list arg);
int vfprintf(FILE *restrict fp, const char * restrict format,va_list arg);
int vsprintf(char *restrict buf,const char * restrict format,va_list arg);
int vsnprintf(char *restrict buf, size_t n, const char *restrict format,va_list arg);
17、格式化输入:
1)使用可变参数族:
#include <stdio.h>
int scanf(const char *restrict format,...);
int fscanf(FILE * restrict fp, const char * restrict format,...);
int sscanf(const char *restrict buf, const char *restrict format,...);
2)使用va_list族
#include <stdio.h>
#include <stdarg.h>
int vscanf(const char *restrict format,va_list arg);
int vfscanf(FILE * restrict fp, const char * restrict format,va_list arg);
int vsscanf(const char *restrict buf, const char *restrict format,va_list arg);
参考:
1、《Linux system programming》
2、《Unix system programming》
3、《Advanced Programming in the Unix Environment》