1. fwide函数
用于设置流的定向。
int fwide(FILE *fp, int mode); //若流是宽定向,返回正值;是字节定向,返回负值;是未定向,返回0。
如若mode是负值,则函数试图使指定的流是字节定向的;如果mode是正值,则函数试图使制定的流是宽定向的;若mode参数值为0,函数不试图设置流的定向,返回标识流定向的值。
2. 缓冲
全缓冲,行缓冲和不带缓冲
标准错误时不带缓冲的,打开至终端设备的流是行缓冲的,其他流是全缓冲的。
void setbuf(FILE *restrict fp, char *restrict buf);
用setbuf打开或者关闭缓冲机制,当buf指向一个长度为BUFSIZE的缓冲区,在此之后的流就是全缓冲的(对于一个与终端设备相关的流,可能不一样);当buf为NULL时,代表关闭缓冲。
int setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);
mode的参数: _IOFBF(全缓冲),_IOLBF(行缓冲),_IONBF(不带缓冲)。
int fflush(FILE *fp); //清洗流,当fp是NULL时,导致所有的输出流被清洗
3. 打开流
fopen,freopen和fdopen。
FILE *freopen(const char *restrict pathname, const char *restrict type, FILE *restrict fp);
FILE *fdopen(int fd, const char *type); //常用于由创建管道和网络通信通道函数返回的描述符。
4. 读和写流
输入函数:getc,fgetc,getchar。
文件的尾端或者出错,调用上面的函数,返回都是EOF;为了区分这个,调用ferror或feof。
int ungetc(int c, FILE *fp); //从流中读取数据后,可以调用ungetc将字符再压送回流
输出函数:putc,fputc,putchar。
5. 每次一行I/O
char *fgets(char *restrict buf, int n, FILE *restrict fp);
char *gets(char *restrict buf); //不太安全,当输入长度大于缓冲区长度
对应的输出函数是fputs和puts。
6. 二进制I/O
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);
7. 定位流
i. ftell和fseek函数,还有rewind函数可将一个流设置到文件的起始位置。
ii. ftello和fseeko函数
iii. fgetpos和fsetpos
8. 格式化I/O
格式化输出
i. int printf(const char *restrict format, ...);
ii. int fprintf(FILE *restrict fp, const char *restrict format,);
iii. int dprintf(int fd, const char *restrict format, ...) //写到指定文件描述符
iX. int spintf(char *restrict buf, const char *restrict format, ...) //将格式化的字符送入数组中
X. int snprintf(char *restrict buf, size_t n, const char *restrict format, ...) //跟上面的类似,就是可以控制数量n,防止溢出。
格式化输入
scanf,fscanf,sscanf函数
9. 实现细节
int fileno(FILE *fp); //获得与流相关联的文件描述符
测试三种标准流:连接终端输入流和输出流都是行缓冲,文件的输入输出流是全缓冲,错误的是不带缓冲的。
#include "apue.h"
void pr_stdio(const char*, FILE*);
int is_unbuffered(FILE*);
int is_linebuffered(FILE*);
int buffer_size(FILE*);
int main(void)
{
FILE *fp;
fputs("enter any character\n", stdout);
if(getchar() == EOF)
err_sys("getchar error");
fputs("one line to standard error\n", stderr);
pr_stdio("stdin", stdin);
pr_stdio("stdout", stdout);
pr_stdio("stderr", stderr);
if((fp = fopen("/etc/passwd", "r")) == NULL)
err_sys("fopen error");
if(getc(fp) == EOF)
err_sys("getc error");
pr_stdio("/etc/passwd", fp);
exit(0);
}
void pr_stdio(const char* name, FILE* fp)
{
printf("stream = %s, ", name);
if(is_unbuffered(fp))
printf("unbuffered");
else if(is_linebuffered(fp))
printf("line buffered");
else
printf("fully buffered");
printf(", buffer size = %d\n", buffer_size(fp));
}
#if defined(_IO_UNBUFFERED)
int is_unbuffered(FILE* fp)
{
return (fp->_flags & _IO_UNBUFFERED);
}
int is_linebuffered(FILE *fp)
{
return (fp->_flags & _IO_LINE_BUF);
}
int buffer_size(FILE *fp)
{
return (fp->_IO_buf_end - fp->_IO_buf_base);
}
#elif defined(__SNBF)
int is_unbuffered(FILE *fp)
{
return (fp->_flags & __SNBF);
}
int is_linebuffered(FILE *fp)
{
return (fp->_flags & __SLBF);
}
int buffer_size(FILE *fp)
{
return (fp->_bf._size);
}
#elif defined(_IONBF)
#ifdef _LP64
#define _flag __pad[4]
#define _ptr __pad[1]
#define _base __pad[2]
#endif
int is_unbuffered(FILE *fp)
{
return (fp->_flag & _IONBF);
}
int is_linebuffered(FILE *fp)
{
return (fp->_flag & _IOLBF);
}
int buffer_size(FILE *fp)
{
#ifdef _LP64
return (fp-_base - fp->_ptr);
#else
return (BUFSIZ);
#endif
}
#else
#error unknown stdio implementation
#endif
10. 临时文件
char *tmpnam(char *ptr); //产生一个与现有文件名不同的一个有效路径名字符串
FILE *tmpfile(void); //创建一个临时二进制文件,在关闭该文件或者程序结束时自动删除这个文件
#include "apue.h"
int main(void)
{
char name[L_tmpnam], line[MAXLINE];
FILE *fp;
printf("%s\n", tmpnam(NULL));
tmpnam(name);
printf("%s\n", name);
if((fp = tmpfile()) == NULL)
err_sys("tmpfile error");
fputs("one line of output\n", fp);
rewind(fp);
if(fgets(line, sizeof(line), fp) == NULL)
err_sys("fgets error");
fputs(line, stdout);
exit(0);
}
相对而言mkstemp更安全一些
char *mkdtemp(char *template); //新建一个临时目录
int *mkstemp(char *template); //新建一个临时文件,返回文件描述符
#include "apue.h"
#include <errno.h>
void make_temp(char *template);
int main()
{
char good_template[] = "/tmp/dirXXXXXX";
char *bad_template = "/tmp/dirXXXXXX";
printf("trying to create first temp file...\n");
make_temp(good_template);
printf("trying to create first temp file...\n");
make_temp(bad_template);
exit(0);
}
void make_temp(char *template)
{
int fd;
struct stat sbuf;
if((fd = mkstemp(template)) < 0)
err_sys("can't create temp file");
printf("temp name = %s\n", template);
close(fd);
if(stat(template, &sbuf) < 0){
if(errno == ENOENT)
printf("file doesn't exist\n");
else
err_sys("stat failed");
} else{
printf("file exists\n");
unlink(template);
}
}
mkstemp与tmpfile不同,创建的临时文件不会自动删除的。
第一种情况因为使用了数组,名字是在栈上分配的;但是第二种情况使用的是指针,在这种情况下,只有指针驻留在栈上。编译器把字符串放在了可执行文件的只读段,当mkstemp函数试图修改字符串时,出现了段错误。
11. 内存流
FILE *fmemopen(void *retrict buf, size_t size, const char *restrict type); //创建内存流
#include "apue.h"
#define BSZ 48
int main()
{
FILE *fp;
char buf[BSZ];
memset(buf, 'a', BSZ - 2);
buf[BSZ - 2] = '\0';
buf[BSZ - 1] = 'X';
if((fp = fmemopen(buf, BSZ, "w+")) == NULL)
err_sys("fmemopen failed");
printf("initial buffer contents: %s\n", buf);
fprintf(fp, "hello, world");
printf("before flush: %s\n", buf);
fflush(fp);
printf("after flush: %s\n", buf);
printf("len of string in buf = %ld\n", (long)strlen(buf));
memset(buf, 'b', BSZ - 2);
buf[BSZ - 2] = '\0';
buf[BSZ - 1] = 'X';
fprintf(fp, "hello, world");
fseek(fp, 0, SEEK_SET);
printf("after fseek: %s\n", buf);
fflush(fp);
printf("len of string in buf = %ld\n", (long)strlen(buf));
memset(buf, 'c', BSZ - 2);
buf[BSZ - 2] = '\0';
buf[BSZ - 1] = 'X';
fprintf(fp, "hello, world");
fclose(fp);
printf("after fclose: %s\n", buf);
fflush(fp);
printf("len of string in buf = %ld\n", (long)strlen(buf));
return (0);
}
输出如下:
initial buffer contents:
before flush: //流冲洗后缓冲区才会变化
after flush: hello, world
len of string in buf = 12
after fseek: bbbbbbbbbbbbhello, world //fseek引起缓冲区冲洗
len of string in buf = 24
after fclose: hello, worldcccccccccccccccccccccccccccccccccc
len of string in buf = 46
还有另外两个函数
FILE *open_memstream(char **bufp, size_t *sizep); //创建的流是面向字节的
FILE *open_wmemstream(wchar_t **bufp, size_t *sizep); //创建的流是面向宽字节的
p173