流和FILE对象
对于ASCII
字符集,一个字符用于一个字节表示。对于国际字符集,一个字符可用于多个字节表示。标准I/O文件流可用于单字节或多字节(“宽”)字符集。流的定向(stream’s orientation)决定了所读、所写的字符是单字节还是多字节。
1 2 3 4 5 6 7 8 9 10 11
| #include <stdio.h> #include <wchar.h>
int fwide(FILE *fp, int mode);
|
标准输入、标准输出和标准错误
可以通过预定义文件指针stdin
、stdout
和stderr
加以引用。
缓冲
- 全缓冲。在这种情况下,在填满标准I/O缓冲区才进行实际I/O操作。
冲洗(flush)说明标准I/O缓冲区的写操作。也可以直接调用fflush
函数冲洗一个流。在终端驱动程序中,flush(刷清)
表示丢弃已存储在缓冲区中的数据。
- 行缓冲。在这种情况下,当在输入和输出中遇到换行符时,标准I/O库执行I/O操作。
- 不带缓冲。标准I/O库不对字符进行缓冲存储。标准错误流
stderr
通常是不带缓冲的。
1 2 3 4 5 6 7
| #include <stdio.h>
void setbuf(FILE *restrict fp, char *restrict buf); void setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);
|
这两个函数可用于更改缓冲类型。
_IOFBF
全缓冲
_IOLBF
行缓冲
_IONBF
不带缓冲
1 2 3 4
| #include <stdio.h>
int fflush(FILE *fp);
|
打开流
1 2 3 4 5 6
| #include <stdio.h>
FILE *fopen(const char *restrict ptahname, const char *restrict type); FILE *freopen(const char *restrict pathname, const char *restrict type, FILE *restrict fp); FILE *fdopen(int fd, const char *type);
|
fopen
函数打开路径名为pathname
的一个指定的文件。
freopen
函数在一个指定的流上打开一个指定的文件,如若该流已经打开,则先关闭该流。
fdopen
函数取一个已有的文件描述符,并使一个标准的I/O流与该描述符相结合。
调用fclose
关闭一个打开的流。
1 2 3 4
| #include <stdio.h>
int fclose(FILE *fp);
|
在该文件被关闭之前,冲洗缓冲区中的输出数据。
读和写流
- 输入函数
1 2 3 4 5 6
| #include <stdio.h>
int getc(FILE *fp); int fgetc(FILE *fp); int getchar(void);
|
函数getchar
等同于getc(stdin)
。前两个函数的区别是,getc
可被实现为宏,而fgetc
不能实现为宏。
1 2 3 4 5 6
| #include <stdio.h>
int ferror(FILE *fp); int feof(FILE *fp);
void clearerr(FILE *fp);
|
为了区别是到达文件结尾还是出错。
例程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <stdio.h> int main(void) { FILE*stream; stream= fopen("DUMMY.FIL","w"); (void) getc(stream); if(ferror(stream)) { printf("ErrorreadingfromDUMMY.FIL\n"); clearerr(stream); } fclose(stream); return0; }
|
从流中读取数据后,可以调用ungetc
将字符再压送回流中。
1 2 3 4
| #include <stdio.h>
int ungetc(int c, FILE *fp);
|
- 输出函数
1 2 3 4 5 6
| #include <stdio.h>
int putc(int c, FILE *fp); int fputc(int c, FILE *fp); int putchar(int c);
|
每次一行I/O
下面两个函数提供每次输入一行的功能。
1 2 3 4 5
| #include <stdio.h>
char *fgets(char *restrict buf, int n, FILE *restrict fp); char *gets(char *buf);
|
fgets
函数一直读到下一个换行符为止,但是不超过n-1个字符,读入的字符被送入缓冲区。
fputs
和puts
提供每次输入一行的功能。
1 2 3 4 5
| #include <stdio.h>
int fputs(const char *restrict str, FILE *restrict fp); int puts(const char *str);
|
函数fputs
将一个以null字节终止的字符串写到指定的流,尾端的终止符null不写出。
二进制I/O
1 2 3 4 5
| #include <stdio.h>
size_t fread(void *restrict ptr, size_t size, size_t nboj, FILE *restrict fp); size_t fwrite(const void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp);
|
指定size为每个数组元素的长度,nobj为欲写元素个数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| float data[10];
if (fwrite(&data[2], sizeof(float), 4, fp) != 4) err_sys("fwrite error");
struct { short count; long total; char name[NAMESIZE]; } item;
if (fwrite(&item, sizeof(item), 1, fp) != 1) err_sys("fwrite error");
|
使用二进制I/O的基本问题,它只能用于读在同一个系统上已写的数据
定位流(Positioning a Stream)
1 2 3 4 5 6 7 8 9
| #include <stdio.h>
long ftell(FILE *fp);
int fseek(FILE *fp, long offset, int whence);
void rewind(FILE *fp);
|
whence
的值与lseek
函数的相同:SEEK_SET
表示从文件的起始位置开始,SEEK_CUR
表示从当前文件位置开始,SEEK_END
表示从文件的尾端开始。offset
,文件偏移量。
1 2 3 4 5 6 7
| #include <stdio.h>
off_t ftello(FILE *fp);
int fseeko(FILE *fp, off_t offset, int whence);
|
1 2 3 4 5
| #include <stdio.h>
int fgetpos(FILE *restrict fp, fpos_t *restrict pos); int fsetpos(FILE *fp, const fpos_t *pos);
|
格式化I/O
- 格式化输出
1 2 3 4 5 6 7 8 9 10 11 12
| #include <stdio.h>
int printf(const char *restrict format, ...); int fprintf(FILE *restrict fp, const char *restrict format, ...); int dprintf(int fd, const char *restrict format, ...);
int sprintf(char *restrict buf, const char *restrict format, ...);
int snprintf(char *restrict buf, size_t n, const char *restrict format, ...);
|
printf将格式化数据写到标准输出,fprintf写至指定的流,dprintf写至制定的文件描述符,sprintf将格式化的字符送入数组buf。
下面5种printf
族的变体,将可变参数表(…)替换成了arg。
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include <stdarg.h> #include <stdio.h>
int vprintf(const char *restrict format, va_list arg); int vfprintf(FILE *restrict fp, const char *restrict format, va_list arg); int vdprintf(int fd, 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);
|
- 格式化输入
1 2 3 4 5 6
| #include <stdio.h>
int scanf(const char *restrict format, ...); int fscanf(FILE *restrict fp, const char *restrict format, ...); int sscanf(const *restrict buf, const char *restrict format, ...);
|
临时文件
1 2 3 4 5 6 7
| #include <stdio.h>
char *tmpnam(char *ptr);
FILE *tmpfile(void);
|
tmpnam
函数产生一个与现有文件名不同的一个有效路径名字符串。如果ptr
是NULL,则产生的路径名存放在一个静态区中,指向该静态区的指针作为函数值返回。后续调用tmpnam
会从写该静态区。如过ptr
不是NULL,则认为它应该指向长度至少是L_tmpnam
个字符的数组。所产生的路径名存放在该数组中,ptr
也作为函数值返回。
tmpfile
创建一个临时二进制文件(wb+),在关闭该文件或程序结束时将自动删除这种文件。
1 2 3 4 5 6 7
| #include <stdlib.h>
char *mkdtemp(char *template);
int mkstemp(char *template);
|
mkdtemp
函数创建了一个目录,该目录有唯一的名字;mkstemp
函数创建了一个文件,该文件有一个唯一的名字。名字是通过template字符串进行选择的。这个字符串后6位设置为XXXXXX的路径名。
内存流
1 2 3 4
| #include <stdio.h>
FILE *fmemopen(void *restrict buf, size_t size, const char *restrict type);
|
fmemopen
函数允许调用者提供缓冲区用于内存流:buf
参数指向缓冲区的开始位置,size
参数指定了缓冲区大小的字节数。
1 2 3 4 5 6
| #include <stdio.h> FILE *open_memstream(char **bufp, size_t *sizep);
#include <wchar.h> FILE *open_wmemstream(wchar_t **bufp, size_t *sizep);
|
open_memstream
函数创建的流是面向字节的,open_wmemstream
函数创建的流是面向宽字节的。
- 创建的流只能打开;
- 不能指定自己的缓冲区,但可以分别通过
bufp
和sizep
参数访问缓冲区地址和大小;
- 关闭后需要自行释放缓冲区;
- 对流添加字节会增加缓冲区大小。