AUPE学习第五章------标准I/O库

时间:2022-12-26 10:38:06

基本上所有的操作系统都实现了标准I/O库。

5.2流和FILE对象

所有的I/O函数都是针对文件描述符的。当文件打开时,就返回一个文件描述符,然后用这个文件描述符做后续的I/O操作。
标准I/O库的操作是围绕流来进行的,当标准I/O库打开一个文件的时候,一个文件已经与一个流关联了。
我们可以用freopen函数来清除一个流定向,然后用fwide函数来设置一个流定向。
int   fwide(FIFE  *fd,  int  mode)

5.4缓冲

标准I/O库提供缓冲的目的是尽可能减少使用read和write函数调用的次数。
第一次执行I/O操作时,相关标准I/O函数通常会调用malloc获得需要使用的缓冲区。
缓冲类型:全缓冲,行缓冲,不带缓冲。
一般标准出错时不带缓冲的,打开至终端设备的流是行缓冲的。其他则都是全缓冲的。
可以用下面的函数改变流的缓冲类型:
void    setbuf(FIFE  *restrict  fp,   char  *restrict  buf)
int       setvbuf(FIFE *restrict  fp, char  *restrict  buf,  int  mode, size_t  size);
任何时候,我们都可以通过下面的函数强制冲洗一个流:
int    fflust(FILE  *fp)
该函数导致所有的数据都被传至内核。

5.5打开流

下列的函数可以用于打开一个标准的I/O流:
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);
调用fclose函数关闭一个打开的流。
int   fclose(FILE   *fp)
在一个文件被关闭前,冲洗刷新区中的输出数据。

5.6读和写流

一旦打开了一个流,就可以用三种类型的I/O进行操作:
1每次一个字符的I/O。
2每次一行的I/O。
3直接I/O。
一下三个函数可以用于一次读一个字符;
int   getc(FILE *fp)
int   fgetc(FILE *fp)
int   getchar(void)
若成功返回下一个字符,若达到文件结尾或者出错返回EOF。

可以用函数ferror来判断流是否出错
int   ferror(FILE *fp)
可以用feof函数判断是否到达文件流的结尾。
int   feof(FILE  *fp)
可以用下面三个函数实现输出功能:
int  putc(int   c,  FILE  *fd)
int  fputc(int   c,  FILE  *fd)
int  putchar(int  c)
其中putchar(c) 等效于 putc(c ,  stdout)

5.7每次一行I/O

下面的函数提供每次输入一行的功能:
char  *fgets(char  *restrict  buf,  int  n,  FILE  *restrict  fp)
char  *getc(char  *buf)
两个函数若成功,则返回buf,若已经到达文件结尾或者出错则返回NULL。
buf是存储数据的缓冲区的地址。
fputs和puts提供每次输出一行的功能。
int   fputs(const   char  *restrict  str,  FILE  *restrict  fp)
次函数每次将一个null终止符的字符串写到指定的流,尾端的终止符null不写出。
puts函数将一个以null终止的的字符串写入标准输出。
int   puts(const  char  *str)
getc和putc实例:
getctest.c:
#include "apue.h"

int main(void)
{
   int  c;
   while((c = getc(stdin)) != EOF)
        if (putc(c, stdout) ==EOF)
            err_sys("output error");
        if (ferror(stdin))
            err_sys("input error");
    exit(0);
}
运行结果如下:
[root@localhost apue]# vim getctest.c
[root@localhost apue]# gcc -o getctest.out getctest.c 
[root@localhost apue]# ./getctest.out 
huang
huang


chengdu
chengdu

fgets和fputs函数实例:
fgetstest.c:
#include "apue.h"

int main(void)
{
    char buf[MAXLINE];
    while (fgets(buf, MAXLINE, stdin) != NULL)
        if (fputs(buf, stdout) == EOF)
            err_sys("output error");
    if (ferror(stdin))
        err_sys("input error");
    exit(0);
}

运行结果:
[root@localhost apue]# vim fgetstest.c
[root@localhost apue]# gcc -o fgetstest.out fgetstest.c 
[root@localhost apue]# ./fgetstest.out 
huiang
huiang


gd  
gd

5.9二进制I/O

由于前面的I/O函数的缺陷,我们可以用下面的函数做二进制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)
返回值是读或者写的对象数。

5.10定位流

定位I/O流有留个函数:
第一组:
long    ftell(FILE  *fp)
int   fseek(FILE *fp,long   offset,  int   whence)
第二个参数是偏移量大小,第三个参数是SEEK_SET,SEEK_CUP,SEEK_END中的一个。
第二组:
off_t   ftello(FILE  *fd)
int  fseeko(FILE *fd,  off_t  offset,  int   whence)
除了offset是off_t类型以外,ftello函数和ftell相同。fseeko和fseek相同。
第三组:
int   fgetpos(FILE  *restrict  fp,  fpos_t  *restrict   pos)
int   fsetpos(FILE   *fp,  const  fpos_t  *pos)
如果问了程序的可移植性,最好用第三组。第一个函数把文件的位置存入pos变量。

5.11格式化I/O

格式化输出函数有下面8个,其中后面四个是变体,只是最后一个可变参数形式改变。
int  printf(const  char  *restrict  format, ...)
int  fprintf( FILE *restrict  fp,  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,  ...)
四种变体:这四个变体是ISO  C标准中关于可变参数的定义实现。在头文件<stdarg.h>中定义。
int  vprintf(const  char  *restrict  format,    var_list arg)
int  vfprintf( FILE *restrict  fp,  const  char  *restrict   format,    var_list arg)
int  vsprintf(  char  *restrict  buf,  const  char  *restrict format,   var_list arg)
int   vsnprintf( char  *restrict  buf,  size_t  n, const  char  *restrict  format,  var_list arg)

格式化输入有三个scanf函数:
int   scanf(const  char  *restrict  format, ...)
int  fscanf(FILE  *restrict   fp,  const  char  *restrict  format,   ...)
int   sscanf(const  char  *restrict   buf,  const   char  *restrict,  ...)
三种变体:这三个变体是ISO  C标准中关于可变参数的定义实现。在头文件<stdarg.h>中定义。
int   scanf(const  char  *restrict  format,   va_list  arg)
int  fscanf(FILE  *restrict   fp,  const  char  *restrict  format,    va_list  arg)
int   sscanf(const  char  *restrict   buf,  const   char  *restrict,  va_list  arg)

5.12实现细节

在unix系统中,标准I/O库都需要调用第三章中的例程。每个标准I/O流都有一个与其相关联的文件描述符。可以对一个流
调用fileno函数以获得其描述符。
int   fileno(FILE  *fp)
例程:对每个标准I/O流打印缓冲状态信息。
ioprint.c:
#include "apue.h"

void  pr_stdio(const  char *,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/motd", "r")) == NULL)
                err_sys("fopen error");
        if (getc(fp) == EOF);
                err_sys("getc error");
        pr_stdio("/etc/motd",fp);
        exit(0);
}
void pr_stdio(const char *name, FILE *fp)
{
        printf("stream = %s, ",name);
        if (fp->_IO_file_flags & _IO_UNBUFFERED)
                printf("unbuffered");
        else if (fp->_IO_file_flags & _IO_LINE_BUF)
                printf("line buffered");
        else
                printf("fully buffered");
        printf(", buffer size = %d\n", fp->_IO_buf_end - fp->_IO_buf_base);
}

运行结果:
[root@localhost apue]# ./ioprint.out 
enter any character
nihao
one line to standard error
stream = stdin, line buffered, buffer size = 1024
stream = stdout, line buffered, buffer size = 1024
stream = stderr, unbuffered, buffer size = 1
getc error: Success

5.13临时文件

下面两个函数可以用于帮助键临时文件:
char   *tmpnam(char   *ptr);
char   *tmpfile(void);
第一个函数返回路径名的指针。如果ptr是NULL。则所产生的路径名存放在静态去中。
第二个函数返回文件指针,错误返回NULL。
函数实例:
tmptest.c:
#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);
}
运行结果:
[root@localhost apue]# gcc -o tmptest.out tmptest.c 
/tmp/ccx3bsSv.o: In function `main':
tmptest.c:(.text+0x299): warning: the use of `tmpnam' is dangerous, better use `mkstemp'
[root@localhost apue]# ./tmptest.out 
/tmp/file7mn10s
/tmp/filetrhn25
one line of output

上面的函数一个用于建临时目录,一个用于建临时文件。下面这个函数一次性搞定:
char  *tempnam(const  char   *directory,  const  char  prefix);
第一个参数是目录名,第二个参数是指定文件的前几个字母。
tempnam.c:
#include "apue.h"
int main(int argc,char *argv[])
{
        if (argc != 3)
                err_quit("usage: a.out  <dirname>  <prefix>");
        printf("%s\n", tempnam(argv[1][0] != ' ' ? argv[1] : NULL, argv[2][0] != ' '? argv[2] : NULL));
        exit(0);
}
运行结果:
[root@localhost apue]# gcc tempnam.c 
/tmp/ccg6BuVi.o: In function `main':
tempnam.c:(.text+0x305): warning: the use of `tempnam' is dangerous, better use `mkstemp'
[root@localhost apue]# ./a.out 
usage: a.out  <dirname>  <prefix>
[root@localhost apue]# ./a.out  /home/huangcd TEMP
/home/huangcd/TEMPzyiJUu
[root@localhost apue]# ./a.out  /home/huangcd " "
/home/huangcd/fileJn5HuC
[root@localhost apue]# ./a.out " "  TEMP
/tmp/TEMPA04EcN
我们也可以用mkstemp函数。功能类似与tmpfile。但是返回的是文件描述符。
int   mkstemp(char  *template)
如果成功返回文件描述符,错误返回-1。
此函数创建的文件不可自动删除。