APUE: Standard I/O Library

流和FIFE对象

标准I/O文件流能够处理单字节和宽字节. 函数fwide用于设置流的方向:数组

#include <stdio.h>ide

#include <wchar.h>函数

int fwide(FILE *fp, int mode);spa

            returns: 宽字节流则返回正数, 单字节流则返回负数, 没有则返回0.指针

若是mode为负数, 则函数尝试将流设置为单字节.rest

若是mode为正数, 则函数尝试将流设置为宽字节.code

若是mode为0, 则保持原样.对象

当咱们使用fopen打开一个流时, 它将返回一个指针指向FILE对象. 此对象包含了流的所有信息: 文件描述符, 指针指向一个存储流的Buffer, Buffer的大小, 已读取流的大小, 错误标志等等.ci

缓冲(Buffering)

标准I/O提供三种类型的缓冲:字符串

1. 全缓冲: 在填满标准I/O缓冲区后才进行实际I/O操做.

2. 行缓冲: 输入和输出遇到换行符时候, 标准I/O执行I/O操做.

3. 不带缓冲.

一般, 系统默认是:

1. 标准错误是不带缓冲的.

2. 如如果涉及终端设备的其余流,则它们是行缓冲的;不然是全缓冲的。

经过函数setbuf来改变流的模式:

#include <stdio.h>

void setbuf(FILE *restrict fp, char *restrict buf);

int setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);

                        returns: 成功返回0, 失败返回非0.

这些函数必须在流被打开,后而且对流操做以前调用.

使用setbuf函数, 咱们能够打开或关闭缓冲. 打开缓冲, 则buf的长度必须为BUFSIZ, 默认状况下为全缓冲, 但若是流关联到终端则会被修改成行缓冲. 若是关闭缓冲, 则buf设置为NULL.

使用setvbuf函数, 咱们能够显式设置缓冲类型:

_IOFBF: 全缓冲

_IOLBF: 行缓冲

_IONBF: 非缓冲

任何状况下, 咱们可使用如下函数强制刷新缓冲:

#include <stdio.h>

int fflush(FILE *fp);

            returns: 成功返回0, 失败返回EOF

打开一个流

如下函数打开一个标准的I/O流:

#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);

                    returns: 成功返回文件指针, 失败返回NULL

而type有如下类型:

type 含义
r, rb
w, wb 文件截断为0或者打开文件用于写
a, ab 打开文件追加至末尾, 或者建立文件用于写
r+, r+b, rb+ 读写
w+, w+b, wb+ 文件截断为0或建立文件用于读写
a+, a+b, ab+ 打开文件用于读, 或者在文件末尾写入

b用于代表是二进制文件仍是文本文件, 可是对于Unix来讲二进制文件和文本文件并没有区别, 因此b参数无任何影响.

使用fclose关闭一个打开流:

#include <stdio.h>

int fclose(FILE *fp);

读取或写入流

如下函数容许咱们一次读取一个字符:

#include <stdio.h>

int getc(FILE *fp);

int fgetc(FILE *fp);

int getchar(void);

        returns: 成功返回下一个字符, 失败或读取完毕返回EOF

getchar函数等价于getc(stdin). 而getc和fgetc不一样之处在于: getc为宏定义, 而fgetc为函数.

用于不管出错仍是读取完毕, 均返回EOF, 因此如下两个函数用于辨别它们:

#include <stdio.h>

int ferror(FILE *fp);

int feof(FILE *fp);

            returns: 成功返回非0, 失败返回0

错误标志和EOF标志均会绑定到FILE对象上, 使用clearerr来清除:

void clearerr(FILE *fp);

在读取一个流以后, 咱们可使用ungetc将字符push back.

#include <stdio.h>

int ungetc(int c, FILE *fp);

        returns: 成功返回c, 失败返回EOF

如下函数容许咱们一次写入一个字符:

#include <stdio.h>

int putc(int c, FILE *fp);

int fputc(int c, FILE *fp);

int putchar(int c);

                returns: 成功返回c, 失败返回EOF

一个实际的例子:

#include <stdio.h>

int main(void)
{
    unsigned int c;
    while ((c = getc(stdin)) != EOF) {
        putc(c, stdout);
    }
    return 0;
}

咱们可使用如下函数读取和写入一行数据:

#include <stdio.h>

char *fgets(char *restrict buf, int n, FILE *restrict fp);

char *gets(char *buf);

            returns: 成功返回buf, 失败或到文件末尾返回NULL.

int fputs(const char *restrict str, FILE *restrict fp);

int puts(const char *str);

            returns: 成功返回非负值, 失败返回EOF

fgets用于读取一行, 但buf长度不可大于n.

gets函数不推荐使用, 由于没肯定所读取数据的长度, 容易致使溢出或者数据丢失.

而puts也不推荐使用, 由于它写入的字符串不带null.

一个实际的例子:

#include <stdio.h>

#define BUFFSIZE 4096
int main(void)
{
    unsigned int arr[BUFFSIZE];
    while (fgets(arr, BUFFSIZE - 1, stdin) != NULL) {
        fputs(arr, stdout);
    }
    return 0;
}

二进制I/O

在读取二进制文件时候咱们没法使用getc/putc, 也没法使用fputs/fgets, 应该使用fread/fwrite:

#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);

                returns: 读取或写入的个数

1--读写一个二进制数组。例如,为了将一个浮点数组的第2~5个元素写至一个文件上,则代码以下:

float data[ 10 ];
if (fwrite(&data[2], sizeof(float), 4, fp ) != 4 )
    printf("write error\n");


2--读写一个结构,则代码以下:

struct {
    short count;
    long total;
    char name[ NAMESIZE ];
} item;
if ( fwrite(&item, sizeof(item), 1, fp ) != 1  )
    printf("write error\n");


    如下程序将结构写入文件中,而后复制文件再读取出来:

#include <stdio.h>

struct people{
	char	*name;
	int	age;
};
int main(void)
{
	int	c;
	char	buf[ 10 ];
	FILE *fp1 = fopen("people", "w+");
	FILE *fp2 = fopen("people.foo","w+");
	if (NULL == fp1 || NULL == fp2) {
		printf("open file error\n");
		return 1;
	}
	struct people p1;
	struct people p2;
	struct people p3;
	struct people p4;
	p1.name = "小雷";
	p1.age = 25;
	p2.name = "小猫咪";
	p2.age = 24;

	if (fwrite(&p1, sizeof(p1), 1, fp1) != 1) {
		printf("write error\n");
		return 1;
	}
	if (fwrite(&p2, sizeof(p2), 1, fp1) != 1) {
		printf("write error\n");
		return 1;
	}
	
	if (fseek(fp1, 0, SEEK_SET) != 0) {
		printf("fseek error\n");
		return 1;
	}	
	while ((c = getc(fp1)) != EOF) {
		if (putc(c, fp2) == EOF) {
			printf("write error\n");
			return 1;
		}
	}

	if (fseek(fp2, 0, SEEK_SET) != 0) {
		printf("fseek error\n");
		return 1;
	}
	if (fread(&p3, sizeof(p3), 1, fp2) != 1) {
		printf("read error\n");
		return 1;
	}
	if (fread(&p4, sizeof(p4), 1, fp2) != 1) {
		printf("read error\n");
		return 1;
	}
	printf("name is:%s, age is:%d\n", p3.name, p3.age);
	printf("name is:%s, age is:%d\n", p4.name, p4.age);

	return 0;
}

程序输出:

leicj@leicj:~/test$ ./a.out
name is:小雷, age is:25
name is:小猫咪, age is:24

临时文件

#include <stdio.h>

#define MAXLINE 4096
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) {
        printf("tmpfile error\n");
        return 1;
    }
    fputs("one line of output\n", fp);
    rewind(fp);
    if (fgets(line,sizeof(line), fp) == NULL) {
        printf("fgets error\n");
        return 1;
    }
    fputs(line, stdout);

    return 0;
}

程序输出:

leicj@leicj:~/test$ ./a.out
/tmp/fileUmW8wA
/tmp/fileBpa2Jr
one line of output