第五章 标准I/O库

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




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值