Unix环境高级编程学习笔记(三)
第五章 标准IO库
1、流和FILE对象:
有两个函数可以改变流的定向:freopen函数可以清除一个流的定向;fwide函数可以设置流的定向
#include<stdio.h>
#include<wchar.h>
int fwide(FILE *fp, int mode); //若流是宽定向的则返回正值,若流是字节定向的则返回值负值,未定向返回0
若mode为负,字节定向;mode为正,宽定向;mode为0,不定向。 fwide并不改变已定向流的定向,无出错返回。
当打开一个流时,标准IO函数fopen返回一个指向FILE对象的指针,该对象是一个结构,包含了标准IO库为管理该流所需的所有信息,包括:文件描述符,该流的缓冲区指针,缓冲区长度等等。我们称FILE*对象为文件指针。
2、标准输入、标准输出、标准出错:stdin、stdout、stderr,定义在<stdio.h>中,注意这三个对象是流。
3、缓冲:全缓冲、行缓冲、不带缓冲
我可以调用以下两个函数更改缓冲类型:#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值
这些函数一定要在流打开后调用,而且要在对该流执行任一操作之前执行。
可以使用setbuf打开或关闭缓冲机制。 使用setvbuf可以精确指定缓冲类型,用mode参数指定:_IOFBF 全缓冲 _IOLBF 行缓冲 _IONBF 不带缓冲
如果指定不带缓冲,则忽略buf和size参数。如果指定行缓冲或全缓冲,buf和size可指定一个缓冲区及其长度。
强制冲洗一个流:#include<stdio.h> int fflush(FILE* fp);
4、打开流:#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);
FILE* fdopen(int filedes, const char* type);
区别:(1)fopen打开一个指定文件 (2)在一个指定的流(如标准输入)上打开指定文件 (3)用文件描述符打开流
type指定打开方式:r或rb(为读打开),w或wb(把文件截短为0,或为写而创建),a或ab(添加;或为写而创建),r+或r+b或rb+(为读和写而打开),w+或w+b或wb+(把文件截短为0,或为读写打开),a+或a+b或ab+(为在文件尾读和写而打开和创建) 默认设置流打开是全缓冲。
可以用fclose关闭一个打开的流:#include<stdio.h> int fclose(FILE* fp);
5、读和写流:(1)每次一个字符;(2)每次一行;(3)、直接IO
(1)#include<stdio.h> int getc(FILE* fp);
int fgetc(FILE *fp);
int getchar(void); 等价于getc(stdin) 成功返回下一个字符,出错或到结尾返回EOF
区分出错或到达文件结尾:#include<stdio.h> int ferror(FILE* fp); int feof(FILE *fp); 若条件为真返回非0值
void clearerr(FILE *fp); 清除出错标志和文件结束标志。
从流中读取数据以后可以调用ungetc将字符再压送回流中:#include<stdio.h> int ungetc(int c, FILE *fp);
(2)输出函数:
对应上面所说的每个输入函数读有一个输出函数:#include<stdio.h> int putc(int c, FILE* fp); int fputc(int c, FILE *fp); int putchar(int c);(等价于putc(c, stdout))
6、每次一行I/O:#include<stdio.h>
char *fgets(char *restrict buf, int n, FILE *restrict fp);
char *gets(char *buf);(不推荐使用)
每次输出一行:int fputs(const char *restrict str, FILE *restrict fp);
int puts(const char *str);(每次会在结尾自动加换行符,虽然安全,但依然推荐避免使用)。
7、标准IO的效率:略。
8、二进制I/O:#include<stdio.h>
size_t fread(void* restrict ptr, 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); 返回读或写的对象个数
用途:(1)读写一个二进制数组:float data[10];
if(fwrite(&data[2], sizeof(float), 4, fp) != 4)
printf("fwrite error!"); (将一个浮点数组写至一个文件上)
(2)读或写一个结构:struct{short count; long total; char name[NAMESIZE];}item;
if(fwrite(&item, sizeof(item), 1, fp) != 1)
printf("fwrite error!");
9、定位流:有三种方法定位标准I/O流:(1)ftell和fseek函数。(2)ftello和fseeko函数。(3)fgetpos和fsetpos函数。 注:要移植到非unix系统,需使用fgetpos和fsetpos
#include<stdio.h>
long ftell(FILE *fp); 成功返回当前位置,出错返回-1
int fssek(FILE *fp, long offset, int whence); 成功返回0,出错赶回非0值
void rewind(FILE *fp); 将流设置到文件的起始位置
whence:SEEK_SET:文件开头;SEEK_CUR:当前文件位置;SEEK_END:文件尾端。
off_t ftello(FILE* fp);
int fseeko(FILE *fp, off_t offset, int whence);
除了offset的类型是off_t而非long外,其他方面于上面两个函数相同。
int fgetpos(FILE *restrict fp, fpos_t *restrict pos);
int fsetpos(FILE *fp, const fpos_t *pos);
10、格式化I/O
(1)格式化输出
#include<stdio.h>
int printf(const char *restrict format, ...); 写到标准输出
int fprintf(FILE *restrict fp, const char *restrict format, ...); 写至指定流
int sprintf(char* restrict buf, const char restrict format, ...); 写至数组buf,不安全
int snprintf(char *restrict buf, size_t n, const char *restrict format, ...); 写至数组buf,安全
格式化说明:p123
还有4个printf的变种:vprintf,vfprintf,vsprintf,vsnprintf
(2)格式化输入
#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, ...);
3个变种:#include<stdio.h> #include<stdarg.h> vscanf, vsscanf, vfscanf
11、实现细节:
获得流的描述符:#include<stdio.h> int fileno(FILE *fp);
程序清单5-3能够用程序确定一个流是全缓冲还是行缓冲还是不带缓冲。
12、临时文件:
有两个函数能够帮助创建临时文件:#include<stdio.h> char *tmpnam(char *ptr); FILE *tmpfile(void);
程序清单5-4演示这两个函数的用法。
两个扩展函数:#include<stdio.h> char *tempnam(const char *directory, const char *prefix); 允许调用者为所产生的路径名指定目录和前缀。
#include<stdlib.h> int mkstemp(char *template); 类似于tmpfile,但是该函数返回文件描述符,而且临时文件不会自动被删除
可以用tmpfile和mkstemp函数取代tmpnam和tempnam函数