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

本文详细介绍了标准I/O函数的功能和使用方法,包括文件流的打开、关闭、读写、定位等操作,以及如何设置缓冲区和处理错误。此外,还提供了多个示例程序来演示这些函数的具体应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

 

相关函数列表

//设置流的定向
#include <stdio.h>
#include <wchar.h>
int fwide(FILE *fp, int mode);

//标准输入,标准输出,标准错误
#include <stdio.h>
//stdin,stdout,stderr

//设置缓冲区
#include <stdio.h>
void setbuf(FILE *restrict fp, char *restrict buf);
int setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size);
//使用setvbuf,我们可以精确的说明所需的缓冲类型,这是用mode参数实现的
//IOEBF  全缓冲
//IOLBF  行缓冲
//IONBF  不带缓冲

//强制刷新流
#include <stdio.h>
int fflush(FILE *fp);

//打开流
#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 fd, const char *type);

//关闭流
#include <stdio.h>
int fclose(FILE *fp);



//读和写流
//一次读一个字符
#include <stdio.h>
int getc(FILE *fp);
int fgetc(FILE *fp);
int getchar(void);

//在调用各种输入输出函数(如putc,getc)时,如果出现错误,除了函数返回值可以反映外,还可以
//使用ferror函数检查
#include <stdio.h>
int ferror(FILE *fp);
int feof(FILE *fp);
void clearerr(FILE *fp);

//从流中读取数据以后,可以调用ungetc将字符再压送回流中
#include <stdio.h>
int ungetc(int c, File *fp);

//输出函数
#include <stdio.h>
int putc(int c, FILE *fp);
int fputc(int c, FILE *fp);
int putchar(int c);


//每次一行I/O
#include <stdio.h>
char *fgets(char *restrict buf, int n, FILE *restrict fp);
char *gets(char *buf);
int fputs(const char *restrict str, FILE *restrict fp);
int puts(const char *str);

//执行二进制I/O
#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);


//定位标准I/O流
#include <stdio.h>
long ftell(FILE *fp);
int fseek(FILE *fp, long offset, int whence);
void rewind(FILE *fp);

//其他,偏移量是off_t而非long意外,ftello和ftell相同,fseeko和fseek相同
#include <stdio.h>
off_t ftello(FILE *fp);
int fseeko(FILE *fp, off_t offset, int whence);

//fgetpos和fsetpos是IOS C标准引入的
#include <stdio.h>
int fgetpos(FILE *restrict fp, fpos_t *restrict pos);
int fsetpos(FILE *fp, const fpos_t *pos);


//格式化I/O
//格式化输出
#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族的变体类似printf,但是可变参数替换成了arg
#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);


//格式化输入
#include <stdio.h>
int scanf(const char *restrict format, ...);
int fscanf(FILE *restrict fp, const char *restrict format, ...);
int sscanf(const char *restrict buf, const char *restrict format, ...);

//与printf类似,scanf族也使用由<stdarg.h>说明的可变长度的参数表
#include <stdarg.h>
#include <stdio.h>
int vscanf(const char *restrict format, va_list arg);
int vfscanf(FILE *restrict fp, const char *restrict format, va_list arg);
int vsscanf(const char *restrict buf, const char *restrict format, va_list arg);


//可以对一个流调用fileno函数获得其描述符
#include <stdio.h>
int fileno(FILE *fp);

//临时文件
#include <stdio.h>
char *tmpnam(char *ptr);
FILE *tmpfile(void);

//Single UNIX Specification为处理临时文件定义了另外两个函数
#include <stdlib.h>
char *mkdtemp(char *template);
int mkstemp(char *template);

//内存流,创建内存流
#include <stdio.h>
#include <wchar.h>
FILE *fmemopen(void *restrict buf, size_t size, const char *restrict type);
FILE *open_memstream(char **bufp, size_t *sizep);
FILE *open)wmemstream(wchar_t **buf, size_t *sizep);


 

 

标准I/O提供了以下三种类型缓冲:

1)全缓冲,在这种情况下,在填满标准I/O缓冲区后才进行实际I/O操作,对于驻留在磁盘上的文件上的文件

    通常都是由标准I/O库实施全缓冲的。在一个流上执行第一次I/O操作时,相关标准I/O函数通常调用malloc

    获得需要使用的缓冲区

2)行缓冲,在这种情况下当输入和输出中遇到换行符时,标准I/O库执行I/O操作。这允许我们一次输出一个

   字符,但只有在写了一行之后才进行实际I/O操作。当流涉及一个终端时,通过使用行缓冲。

3)不带缓冲,标准I/O库不对字符进行缓冲存储。列如用标准I/O函数fputs写15个字符到不带缓冲的流中,

   我们就期望这15个字符能立即输出

标准错误流stderr通常是不带缓冲的,这就是的出错信息可以尽快显示出来,而不管他们是否含有一个换行

  符

ISO C要求下列缓冲特征:

1)当且仅当标准输入和标准输出并不指向交互设备时,它们才是全缓冲的

2)标准错误绝不会是全缓冲的

这两个函数的动作,以及他们的各个选项

函数modebuf缓冲区及长度缓冲类型
setbuf 非空长度为BUFSIZE的用户缓冲区buf全缓冲或行缓冲
同上 NULL无缓冲区不带缓冲
setvbuf_IOFBF非空长度为size的用户缓冲区buf全缓冲
 _IOFBF 合适长度的系统缓冲区buf全缓冲
 _IOLBF 长度为size的用户缓冲区buf行缓冲
 _IOLBF 合适长度的系统缓冲区buf行缓冲
同上_IONBF 无缓冲区不带缓冲

 

 

type参数指定对该I/O流的读,写方式,ISO C规定type参数可以有15种不同的值

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

 

 

打开一个流的6种方式

限制RWar+w+a+

文件必须已存在

    
放弃文件以前的内容    
流可以读  
流可以写 
流只可在尾端写    

 

 

格式化输出,一个转换说明有4个可选部分

%[flags] [fldwidth] [percision] [lenmodifier] convtype

参数说明
fldwidth

最小字段宽度。转换后参数字符数若小于宽度,则多余字符位用空格填充,字段宽度是一个

非负数十进制数,或是一个星号(*)

precision

整型转换后最少输出数字位数,浮点数转换后小数点后的最少位数,字符串转换后最大字节

数。精度是一个点(.) ,其后跟随一个可选的非负数十进制或一个星号(*)

宽度和精度字段两者皆可为*。此时一个整型参数指定宽度或精度的值。该整型参数正好位于

被转换的参数之前。 

lenmodifier参数长度
convtype不可选的,它控制如何解释参数

 

各种标志如下

标志说明
'(撇号)将蒸熟按千位分组字符
-在字段内左对齐输出
+总是显示带符号转换的正负号
(空格)如果第一个字符不是正负号,则在其前面加上一个空格
#指定另一种转换形式(列如,对于十六进制格式,加上0x前缀)
0添加前导0(而非空格)进行填充

 

lenmodifier说明参数长度

长度修饰符说明
hh将相应的参数按signed或unsigned char类型输出
h将相应的参数按signed或unsigned short类型输出
l将相应的参数按signed或unsigned long或宽字符类型输出
ll将相应的参数按signed或unsigned long long类型输出
jintmax_t或uintmax_t
zsize_t
tptrdiff_t
Llong double

convtype不是可选的,它控制如何解释参数

转换类型说明
d,i有符号十进制
o无符号八进制
u无符号是兼职
x,X无符号十六进制
f,F双精度浮点数
e,E指数格式双精度浮点数
g,G根据转换后的值解释为f,F,e或E
 a,A十六进制指数格式双精度浮点数 
c字符(若带长度修饰符l,为宽字符)
 s字符串(若带长度修饰符l,为宽字符) 
P指向void的指针
n

到目前为止,此printf调用输出的字符的数目将被写入到

指针所指向的带符号整型中

 %一个%字符 
C宽字符(XSI扩展,等效于lc)
S宽字符串(XSI扩展,等效于ls)

 

 

 

格式化输入

一个转换说明有三个可选择部分,下面将他们都示于方括号中:

%[*] [fldwidth] [m] [lenmodifier] convtype

参数说明
fldwidth最大宽度(即最大字符数)
lenmodifier要用转换结果赋值的参数大小
convtype

类似printf族的转换类型字段,差别是作为一种选项,

输入中带符号的可赋予无符号类型

m

在字段宽度和长度修饰符之间的可选项m是赋值分配符

可用于%c,%s,以及%[转换符]

 

转换说明中的转换类型

转换类型说明
d有符号十进制,基数为10
i有符号十进制,基数由输入格式决定
O无符号八进制(输入可选的有符号)
u无符号十进制,基数为10(输入可选的有符号)
x,X无符号十六进制(输入可选的有符号)

a,A,e,E

f,F,g,G

浮点数
c字符(若带长度修饰符l ,为宽字符)
s字符串(若带长度修饰符l ,为宽字符串)
[匹配列出的字符序列,以] 终止
]^匹配除列出字符意外的所有字符,以]终止
P指向void的指针
n

将到目前为止该函数调用读取的字符数写入到指针

所指向的无符号整型中

%一个%符号
C宽字符(XSI 扩展,等效于lc)
S宽字符串(XSI 扩展,等效于ls)

 

 

内存流的type参数

type说明
r 或 rb为读而打开
w 或 wb为写而打开
a 或 ab追加,在为第一个null字节处写而打开
r+ 或 r+b 或 rb+为读和写而打开
w+ 或 w+b 或 wb+把文件截断至0长,为读和写而打开
a+ 或 a+b 或 ab+追加,为在第一个null字节处读和谐而打开

 

这些取值对应于基于文件的标准I/O流的type参数取值,但其中还是有差别

1)无论何时以追加写方式打开内存流时,当前文件位置设置为缓冲区中第一个null字节,如果缓冲区中不

   存在null字节,则当前位置就设置为缓冲区结尾的后一个字节。当流并不是以追加方式打开时,当前

   位置设置为缓冲区的开始位置。因为追加写模式通过第一个null字节确定数据的尾端,内存流并不合适

   存储二进制数据

2)如果buf参数是一个null指针,打开流进行读或者写都没有任何意义。因为在这种情况下缓冲区通过

   fmemopen进行分配的,没有办法找到缓冲区的地址,只写方式打开流意味着无法读取已写入的数据,同样

   以读方式打开流意味着只能读取那些我们无法写入的缓冲区中的数据

3)任何时需要增加流缓冲区中数据量以及调用fclose,fflush,fseek,fseeko,以及fsetpos时都会在当前

   位置写入一个null

 

 

 

 

 

标准I/O读写单个字符的列子

#include <fcntl.h>  
#include <stdio.h>  
#include <unistd.h>  
#include <sys/stat.h>  
#include <stdio.h>
int main(int argc, char *argv[]) {
        FILE *file_r;
        FILE *file_w;
        char *path = "a.tar.gz";
        char *dest = "tmp.tar.gz";
        int num;

        if((file_r=fopen(path,"r")) == NULL) {
                printf("fopen %s error\r\n",path);
        }
        if((file_w=fopen(dest,"w+")) ==NULL) {
                printf("fopen %s error\r\n",dest);
        }

        while( (num=getc(file_r)) != EOF ) {
                if(putc(num,file_w) == EOF) {
                        printf("output error\r\n");
                }
        }
        if(ferror(file_r)) {
                printf("intpu error\r\n");
        }

        if(fclose(file_r)==EOF) {
                printf("fclose %s error\r\n",path);
        }
        if(fclose(file_w)==EOF) {
                printf("fclose %s error\r\n",dest);
        }
        return 0;
}

 

 

标准I/O读写(读写一行)

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdio.h>
#define MAXLINE 10240
int main(int argc, char *argv[]) {
        char buf[MAXLINE];
        FILE *file_r;
        FILE *file_w;
        char *src = "man.config";
        char *dest = "buf.config";
        int num;

        if((file_r=fopen(src,"r")) == NULL) {
                printf("fopen %s error\r\n",src);
        }
        if((file_w=fopen(dest,"w+")) == NULL) {
                printf("fopen %s error\r\n",dest);
        }

        while(fgets(buf,MAXLINE,file_r) != NULL) {
                if(fputs(buf,file_w) == EOF) {
                        printf("output error\r\n");
                }
        }

        if(ferror(file_r)) {
                printf("input error\r\n");
        }
        if(fclose(file_r) == EOF) {
                printf("fclose %s error\r\n",src);
        }
        if(fclose(file_w) == EOF) {
                printf("fclose %s error\r\n",dest);
        }
        return 0;
}

 

 

标准I/O读写二进制文件

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
        FILE *file_r;
        FILE *file_w;
        char *src = "a.tar.gz";
        char *dest = "binary.tar.gz";
        char buf[1024];
        int num;

        file_r = fopen(src,"r");
        file_w = fopen(dest,"w+");

        while( (num=fread(buf,sizeof(char),1024,file_r)) > 0) {
                if(fwrite(buf,sizeof(char),num,file_w) != num) {
                        printf("fwrite %s error\r\n",dest);
                }
        }
        if(ferror(file_r)) {
                printf("input error\r\n");
        }
        fclose(file_r);
        fclose(file_w);
        return 0;
}

 

 

测试三个标准流和一个普通文件的缓冲区信息

书中列子buffer_size()函数是返回 (fp->_bf._size),但实际没有这个变量,所以只好改成-1

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdio.h>
static int buffer_size(FILE *fp) {
        return -1;
}

static int is_unbuffered(FILE *fp) {
        return (fp->_flags & _IONBF);
}

static int is_linebuffered(FILE *fp) {
        return (fp->_flags & _IOLBF);
}

static 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));
}

int main(int argc, char *argv[]) {
        FILE *fp;
        fputs("enter ant character\n",stdout);
        if(getchar() == EOF) {
                printf("getchar error\r\n");
        }
        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) {
                printf("\r\nfopen error");
        }
        if(getc(fp) == EOF) {
                printf("getc error");
        }
        pr_stdio("/etc/passwd",fp);
        return 0;
}

打印结果

enter ant character

one line to standard error
stream = stdin,  fully buffered,buffer size = -1
stream = stdout,  fully buffered,buffer size = -1
stream = stderr,  unbuffered,buffer size = -1
stream = /etc/passwd,  fully buffered,buffer size = -1

 

 

创建临时文件,gcc编译时会有一段警告

warning: the use of `tmpnam' is dangerous, better use `mkstemp'

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdio.h>
#define MAXLINE 1024
int main(int argc, char *argv[]) {
        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\r\n");
        }
        fputs("one line of output\n",fp);
        rewind(fp);
        if(fgets(line,sizeof(line),fp) == NULL) {
                printf("fgets error\r\n");
        }
        fputs(line,stdout);
        return 0;
}

 

 

第二种创建临时文件的方式

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
static void make_temp(char *template) {
        int fd;
        struct stat buf;
        if((fd=mkstemp(template)) < 0) {
                printf("mkstemp create error\r\n");
        }
        printf("temp name = %s\n",template);
        close(fd);
        if(stat(template, &buf) < 0) {
                if(errno == ENOENT) {
                        printf("file don't exist\n");
                }
                else {
                        printf("stat failed\r\n");
                }
        }
        else {
                printf("file exists\r\n");
        }
        unlink(template);
}

int main(int argc, char *argv[]) {

        char good_template[] = "/tmp/dirXXXXX";
        char *bad_template = "/tmp/dirXXXXX";
        printf("try to create first temp file...\n");
        make_temp(good_template);
        printf("try to create second temp file...\n");
        make_temp(bad_template);
        return 0;
}

 

 

内存流

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#define BSZ 48
int main(int argc, char *argv[]) {
        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) {
                printf("fmemopen error\r\n");
        }
        printf("initial buffer content: %s\n",buf);
        fprintf(fp, "hello,world");
        fflush(fp);
        printf("after fflush: %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);
        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);
        printf("len of string in buf = %ld\n", (long)strlen(buf));
        return 0;
}

 

 

 

 

 

 

 

参考

FILE结构体的定义

stdio.h

标准I/O替代软件SFIO(还有FIO)

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值