[UNIX环境高级编程] 标准I/O库

1 流和FILE对象

对于标准I/O库,它们的操作是围绕流(stream)进行的,当用标准I/O库打开或者创建一个文件时,我们已使一个流与一个文件相关联。
对于ASCII字符集,一个字符用一个字节表示。对于国际字符集,一个字符可用多个字节表示。标准I/O文件流可用于单字节或多字节字符集。freopen函数清除一个流的定向,fwide函数可用于设置流的定向。

#include 
// A positive return value means wide-character oriented.
// A negative return value means byte oriented. 
// A return value of zero means undecided.
int fwide(FILE* stream, int mode);

根据mode参数的不同,fwide函数执行不同的功能:
[1] 如若mode参数值为负,fwide将试图使指定的流是字节定向的。
[2] 如若mode参数值为正,fwide将试图使指定的流是宽定向的。
[3] 如若mode参数值为0,fwide将不试图设置流的定向,但返回标识该流定向的值。

2 标准输入、标准输出和标准错误

对一个进程预定义了三个流:标准输入、标准输出和标准错误,这三个标准I/O流通过预定义文件指针stdin、stdout和stderr加以引用,被定义在中。

3 缓冲

标准I/O库提供缓冲是为了尽可能减少read和write调用的次数。它也对每个 标准I/O流自动地进行缓冲管理。
标准I/O提供了以下3种类型的缓冲:
[1] 全缓冲:在填满标准I/O缓冲区后才进行实际I/O操作。
[2] 行缓冲:当输入和输出中遇到换行符时,标准I/O库执行I/O操作,当流涉及一个终端时,往往使用行缓冲。
[3] 不带缓冲:标准I/O库部队字符进行缓冲存储。
ISO/C要求下列缓冲特征:
[1] 当且仅当标准输入和标准输出并不指向交互式设备时,他们才是全缓冲的。
[2] 标准错误决不是全缓冲的。
[3] 标准错误时不带缓冲的。
[4] 若是指向终端设备的流,则是行缓冲的,否则是全缓冲的。

对于任何一个给定的流,可以调用下面两个函数更改缓冲类型。

#include 
void setbuf(FILE* stream, char* buf);
int setvbuff(FILE* stream, char* buf, int mode, size_t size);

mode参数:
_IOFBF 全缓冲
_IOLBF 行缓冲
_IONBF 不带缓冲

函数 mode buf 缓冲区及长度 缓冲类型
setbuf 非空 长度为BUFSIZ的用户缓冲区buf 全缓冲或行缓冲
setbuf NULL (无缓冲区) 不带缓冲
setvbuf _IOFBF 非空 长度为size的用户缓冲区buf 全缓冲
setvbuf _IOFBF NULL 合适长度的系统缓冲区buf 全缓冲
setvbuf _IOLBF 非空 长度为size的用户缓冲区buf 行缓冲
setvbuf _IOLBF NULL 合适长度的系统缓冲区buf 行缓冲
setvbuf _IONBF (忽略) (无缓冲区) 不带缓冲

任何时候,我们都可以强制冲洗一个流。

#include 
int fflush(FILE* stream);

此函数使该流所有未写的数据都被传送至内核,若stream是NULL,则此函数将导致所有输出流被冲洗。

4 打开流

下列3个函数打开一个标准I/O流。

#include 
// return a file pointer or NULL if an error occurred.
FILE* fopen(const char* path, const char* mode);
FILE* fdopen(int fd, const char* mode);
FILE* freopen(const char* path, const char* mode, FILE* stream);

这三个函数的区别如下:
[1] fopen函数打开路径名为pathname的一个指定的文件。
[2] freopen函数在一个指定的流上打开一个指定的文件,若流已经打开,则先关闭,若流已经定向,则用freopen清楚定向。一般用于将一个指定的文件打开为一个预定义的流:标准输入、标准输出或标准错误
[3] fdopen函数取一个已有的文件描述符,并使一个标准的I/O流与该描述符相结合。此函数通常用于创建管道和网络通信管道函数返回的描述符

type 说明 open(2)标志
r或rb 为读而打开 O_RDONLY
w或wb 把文件截断至0或为写而创建 O_WRONLY | O_CREAT | O_TRUNC
a或ab 追加或为写而打开 O_WRONLY | O_CREAT | O_APPEND
r+或r+b或rb+ 为读和写而打开 O_RDWR
w+或w+b或wb+ 把文件截断至0或为写而打开 O_RDWR | O_CREAT | O_TRUNC
a+或a+b或ab+ 为在文件尾读和写而打开或创建 O_RDWR | O_CREAT | O_APPEND

对于fdopen函数,type参数的意义有些不同1
当以读和写的方式打开一个文件时具有下列限制
[1] 如果中间没有fflush、fseek、fsetpos或rewind,则在输出后不能直接跟随输入。
[2] 如果中间没有fseek、fsetpos、或rewind,或者一个输入操作没有到达文件尾端,则在输入操作之后不能直接跟随输出。

调用fclose关闭一个打开的流。

#include 
// return 0 or EOF if an error occurred.
inf fclose(FILE* fp);

5 读和写流

一旦打开了流,则可以在3中不同类型的非格式化I/O中进行选择,对其进行读、写操作。
[1] 每次一个字符的I/O,如果流是带缓冲的,则标准I/O函数处理所有缓冲。
[2] 每次一行的I/O。
[3] 直接I/O。

5.1 每次一个字符的I/O

#include 
int get(FILE* stream);
int fgetc(FILE* stream);
int getchar(void);

#include 
int putc(int c, FILE* stream);
int fputc(int c, FILE* stream);
int putchar(int c);

#include 
int ungetc(int c, FILE* stream);

函数getchar等同于getc(stdin),最好使用fgetc函数,更安全。
函数putchar©等同于putc(c, stdout),最好使用fputc函数。
从流中读取数据后,可以调用ungetc将字符再压送回流中。

5.2 每次一行的I/O

#include 
char* gets(char* s);
char* fgets(char* s, int size, FILE* stream);

#include 
int puts(const char* s);
int fputs(const char* s, FILE* stream);

5.3 直接I/O(二进制I/O)

#include 
size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream);
size_t fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream);

// usage
float data[10];
if (fwrite(&data[2], sizeof(float), 4, fp) != 4)
    // error handler.

struct
{
    short count;
    long total;
    char name[255];
} item;
if (fwrite(&item, sizeof(item), 1, fp) != 1)
    // error handler.

6 定位流

#include 
int fseek(FILE* stream, long offset, int whence);
long ftell(FILE* stream);
void rewind(FILE* stream);

int fseeko(FILE* stream, off_t offset, int whence);
off_t ftello(FILE* stream);

int fgetpos(FILE* stream, fpos_t* pos);
int fsetpos(FILE* stream, fpos_t* pos);

7 格式化I/O

7.1 格式化输出

#include 
int printf(const char* format, ...);
int fprintf(FILE* stream, const char* format, ...);
int dprintf(int fd, const char* format, ...);
int sprintf(char* str, const char* format, ...);
int snprintf(char* str, size_t size, const char* format, ...);

#include 
int vprintf(const char* format, va_list ap);
int vfprintf(FILE* stream, const char* format, va_list ap);
int vdprintf(int fd, const char* format, va_list ap);
int vsprintf(char* str, const char* format, va_list ap);
int vsnprintf(char* str, size_t size, const char* format, va_list ap);

具体格式化参数详见格式化字符串参数介绍.

7.2 格式化输入

#include 
int scanf(const char* format, ...);
int fscanf(FILE* stream, const char* format, ...);
int sscanf(const char* str, const char* format, ...);

#include 
int vscanf(cosnt char* format, va_list ap);
int vsscanf(const char* str, const char* format, va_list ap);
int vfscanf(FILE* stream, const char* format, va_list ap);

8 临时文件

#include 
char* tmpnam(char* s);
FILE* tmpfile(void);
  1. 由于该描述符已被打开,所以fdopen函数为写而打开并不截断该文件。另外,标准I/O追加写方式也不能用于创建该文件。 ↩︎

你可能感兴趣的:(UNIX环境高级编程读书笔记)