APUE(5)---标准I/O库 (3)

十、定位流

#include <stdio.h>
long ftell(FILE *fp);
//若成功,返回当前文件位置指示;若出错,返回-1L
int fseek(FILE *fp, long offset, int whence);
//若成功,返回0;若出错,返回-1L
void rewind(FILE *fp);

  对于一个二进制文件,whence可以为SEEK_SET/SEEK_CUR/SEEK_END;对于文本文件,whence只能是SEEK_SET,并且offset只能是:0(后退到文件的起始位置)或是对该文件的ftell所返回的值。使用rewind也可以将一个流设置到文件的起始位置。

#include <stdio.h>
off_t ftello(FILE *fp);
int fseeko(FILE *fp, off_t offset, int whence);
//除了偏移量的类型不同,其余同上
#include <stdio.h>
int fgetpos(FILE *restrict fp, fpos_t *restrict pos);
//将文件位置指示器存入pos指向的对象中
int fsetpos(FILE *fp, const fpos_t *pos);

十一、格式化I/O
  此章节太过繁琐,并且意义不大,以后需要使用再过补充。

十二、实现细节

#include <apue.h>
#include <my_err.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("linebuffered");
    }
    else
    {
        printf("fully buffered");
    }
    printf(", buffer size = %d\n", buffer_size(fp));
}

int is_unbuffered(FILE *fp)
{
    return fp->_flags & _IONBF;
}
int is_linebuffered(FILE *fp)
{
    return fp->_flags & _IOLBF;
}
int buffer_size(FILE *fp)
{
    return fp->_IO_buf_end - fp->_IO_buf_base;
}

5-11 对各个标准I/O流打印缓冲状态信息

十三、临时文件

  ISO C标准I/O库提供了两个函数帮助创建临时文件

#include <stdio.h>
char *tmpnam(char *ptr);
FILE *tmpfile(void);

  tmpnam函数产生一个与现有文件名不同的一个有效路径名字符串,若ptr是NULL,则产生的路径存放在一个静态区,指向该静态区的指针作为函数值返回,后续调用tmpnam时,会重写该静态区;若ptr不是NULL,则认为他应该是长度至少是L_tmpnam的字符数组,用这个名字创建临时文件可能存在问题,因为得到文件名和创建是两个动作,不具有唯一性。tmpfile是直接创建一个临时文件,其操作手法是先调用tmpnam产生一个唯一的路径名,然后创建一个文件,然后立即unlink它。

#include <apue.h>
#include <my_err.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);
}

5-12 tmpnam和tmpfile函数实例

#include <stdlib.h>
char *mkdtemp(char *template);
//若成功,返回指向目录名的指针;若出错,返回NULL
int mkstemp(char *template);
//若成功,返回文件描述符;若出错,返回-1

  mkdtemp函数创建了一个目录,该目录有一个唯一的名字;mkstemp函数创建了一个文件,该文件有一个唯一的名字,名字是通过template字符串进行选择的,与tempfile不同,mkstemp创建的临时文件不会自动删除,如果希望删除,需要自己手动unlink。

#include <apue.h>
#include <my_err.h>
#include <errno.h>

void make_temp(char *template);

int main(void)
{
    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 second temp file...\n");
    make_temp(bad_template);
    exit(0);
}

void make_temp(char *template)
{
     int fd;
     struct stat buf;
     if((fd = mkstemp(template)) < 0)
     {
         err_sys("can‘t create temp file");
     }

     printf("temp name=%s\n", template);
     close(fd);
     if(stat(template, &buf) < 0)
     {
         if(errno == ENOENT)
         {
             printf("file doesn‘t exit\n");
         }
         else
         {
             printf("stat failed\n");
         }
     }
     else
     {
         printf("file exists\n");
         unlink(template);
     }
}

5-13 mkstemp函数的应用

trying to create first temp file...
temp name=/tmp/dirQREuAu
file exists
trying to create second temp file...
Segmentation fault (core dumped)
//第二种情况,指针自身驻留在栈上,编译器把字符串存放在可执行文件的只读段,当mkstemp函数试图修改字符串时,出现了段错误

十四、内存流
  标准I/O库把数据缓存在内存中,因此每次一字符和每次一行的I/O更有效,我们也可以通过调用setbuf或setvbuf函数让I/库使用我们自己的缓冲区在SUSv4中支持了内存流,这就是标准I/O流,虽然仍使用FILE指针进行访问,但其实并没有底层文件,所有的I/O都是通过在缓冲区与贮存之间来回传送字节完成的。有三个函数可用于内存流的创建。

#include <stdio.h>
FILE *fmemopen(void *restrict buf, size_t size, const char *restrict type);

  fmemopen函数允许调用者提供缓冲区用于内存流,buf参数指向缓冲区开始的位置,size参数制订了缓冲区大小的字节数,如果buf参数为空,fmemopen函数分配size字节数的缓冲区,在这种情况下,当流关闭时缓冲区就会被释放。type参数控制如何使用流。其和文件有以下区别:

1.无论何时以追加写方式打开内存流时,当前文件位置设为缓冲区中的第一个null字节,如果缓冲区不存在null字节,则当前位置就设为缓冲区结尾的后一个字节。当流不是以追加写方式打开时,当前位置设置为缓冲区的开始位置。因为追加写模式通过第一个null字节确定数据的尾端,内存流并不适合存储二进制数据。

2.如果buf参数是一个null指针,打开流进行读和写都没有任何意义。

3.任何时候需要增加流缓冲区中数据量以及调用fclose、fflush、fseek、fseeko以及fsetpos时都会在当前位置写入一个null字节

<apue.h>
#include <my_err.h>

#define BSZ 48

int main(void)
{
    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);
    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;
}

5-15 观察内存流的写入操作

#include <stdio.h>
FILE *open_memstram(char **bufp, size_t *size);

#include <wchar.h>
FILE *open_wmemstream(wchar_t **bufp, size_t *sizep);

  open_memstream函数创建的流是面向字节的,open_wmemstream是面向宽字节的。他们同fmemopen函数的区别在于:1)创建的流只能写打开;2)不能制定自己的缓冲区,但可以分别bufp和sizep参数访问缓冲区地址和大小;3)关闭流后需要自动释放缓冲区;4)对流添加字节会增加缓冲区大小。

  使用上面必须遵守的规则:缓冲区地址和长度只有在调用fclose或fflush后才有效;这些值只有在下一次流写入或调用fclose前才有效。使用内存流相比临时文件,会有很大的性能提升。

时间: 2024-10-29 19:05:55

APUE(5)---标准I/O库 (3)的相关文章

APUE:标准I/O库

设置缓冲: #define _IOFBF 0 /* Fully buffered. */ #define _IOLBF 1 /* Line buffered. */ #define _IONBF 2 /* No buffering. */ void setbuf (FILE *__restrict __stream, char *__restrict __buf); int setvbuf (FILE *__restrict __stream, char *__restrict __buf, i

APUE学习笔记:第五章 标准I/O库

5.1 引言 标准I/O库处理很多细节,例如缓冲区分配,以优化长度执行I/O等.这些处理不必担心如何使用正确的块长度.这使得它便于用户使用,但是如果不较深入地了解I/O库函数的操作,也会带来一些问题 5.2 流和FILE对象 对于ASCII字符集,一个字符用一个字节表示.对于国际字符集,一个字符可用多个字节表示.标准I/O文件流可用于单字节或多字节字符集. 流的定向决定了所读.写的字符是单字节还是多字节的.当一个流最初被创建时,它并没有定向.如若在未定向的流上使用一个多字节I/O函数,则将该流的

APUE读书笔记-第五章 标准I/O库

今天草草的把第四章结了,后面的内容分析的也不是很详细,就连书中的例子都没有怎么实验,还是等以后有机会吧. 从5.3节开始研究起吧,这一节主要谈了一个进程预定义的3个流,分别是标准输入.标准输出和标准错误,通过stdin.stdout.stderr引用.这里要和进程中的文件描述符STDIN_FILENO.STDOUT_FILENO.STDERR_FILENO相区分. /* Standard streams. */ extern struct _IO_FILE *stdin; /* Standard

APUE(5)---标准I/O库 (2)

六.读和写流 一旦打开了流,则可在3种不同类型的非格式化I/O中进行选择,对其进行读.写操作:1)每次一个字符的I/O,一次读或写一个字符,如果刘时代缓冲的,则标准I/O函数处理所有缓冲:2)每次一行的I/O.如果想要一次读或写一行,则使用fgets和fputs.每行都以一个换行符终止.当调用fgets时,应说明能处理的最大行长.3)直接I/O(这个术语来自ISO C标准,有时也被称为二进制I/O,一次一个对象的I/O.面向记录的I/O或面向结构的I/O).fread和fwrite函数支持这种类

APUE 学习笔记(四) 标准I/O库

1.流与FILE对象 unix I/O系统调用都是针对文件描述符的 标准C的I/O函数都是针对流(文件指针)的,我们使用一个流与一个文件相关联 2.缓冲 标准I/O库提供缓冲的目的就是尽可能减少read和write系统调用的使用次数 标准I/O提供三种类型的缓冲: (1) 全缓冲:在填满标准I/O缓冲区后才进行实际I/O操作,磁盘上的文件通常是全缓冲,第一次I/O操作时调用malloc获得需要使用的缓冲区 (2)行缓冲:输入输出遇到换行符时,标准I/O库执行I/O操作.涉及终端时(标准输入和标准

UNIX高级环境编程(7)标准IO函数库 - 二进制文件IO,流定位,创建临时文件和内存流

? 1 二进制IO(Binary IO) 在前一篇我们了解了逐字符读写和逐行读写函数. 如果我们在读写二进制文件,希望以此读写整个文件内容,这两个函数虽然可以实现,但是明显会很麻烦且多次循环明显效率很低. 为了应对这种场景,标准IO库提供了fread和fwrite函数. 函数声明: #include <stdio.h> size_t fread(void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp); size_t fw

Linux C 文件操作 -- 系统调用(open(),read()...) 和 标准I/O库(fopen(),fread()...)

一.什么是文件 在讲述文件操作之前,我们首先要知道什么是文件.看到这个问题你可能会感觉到可笑,因为对于用过计算机的人来说,文件是最简单不过的概念了,例如一个文本是一个文件,一个work文档是一个文件等.但是在Linux中,文件的概念还远不止于这些,在Linux中,一切(或几乎一切)都是文件.文件包括很多的内容,例如:大家知道的普通文件是文件,目录也是一个文件,设备也是一个文件,管道也是一个文件等等.对于目录.设备这些的操作也可以完全等同于对纯文本文件的操作,这也是Linux非常成功的特性之一吧.

C++标准I/O库:iostream, fstream, sstringstream

在写代码的过程中.我们最常做的事就是io操作,不管是对控制台,还是文件.但一段时间不写代码就忘了,这里理一下C++标准I/O库的详细类和操作. C++的标准I/O库包含我们常常使用的iostream,fstream.以及不太常常使用的stringstream.前两者是对控制台和文件的I/O操作,stringstream则能够使用I/O操作对内存中的数据进行格式化操作. C++的标准I/O操作相对与C来说,更加的简明,安全,但运行效率会有所下降. 标准I/O库类继承体系              

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

前言:我想大家学习C语言接触过的第一个函数应该是printf,但是我们真正理解它了吗?最近看Linux以及网络编程这块,我觉得I/O这块很难理解.以前从来没认识到Unix I/O和C标准库I/O函数压根不是一码事.Unix I/O也叫低级I/O,也叫Unbuffered I/O,是操作系统内核部分,也是系统调用:而C标准I/O函数相对也成Buffered I/O,高级I/O,一般是为了效率考虑对这些系统调用的封装.以前使用getchar()经常为输入完后的回车而出错.那是不理解标准I/O实现时的