• 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
    ", stdout);
        if(getchar() == EOF)
        {
            err_sys("getchar error");
        }
    
        fputs("one line to standard error
    ", 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
    ", 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
    ", tmpnam(NULL));
    
        tmpnam(name);
        printf("%s
    ", name);
    
        if((fp = tmpfile()) == NULL)
        {
            err_sys("tmpfile error");
        }
    
        fputs("one line of output
    ", 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...
    ");
        make_temp(good_template);
    
        printf("trying to create second temp file...
    ");
        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
    ", template);
         close(fd);
         if(stat(template, &buf) < 0)
         {
             if(errno == ENOENT)
             {
                 printf("file doesn't exit
    ");
             }
             else
             {
                 printf("stat failed
    ");
             }
         }
         else
         {
             printf("file exists
    ");
             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] = '';
        buf[BSZ-1] = 'X';
        if((fp = fmemopen(buf, BSZ, "w+")) == NULL)
        {
            err_sys("fmemopen failed.");
        }
        printf("initial buffer contents:%s
    ", buf);
        fprintf(fp, "hello world");
        printf("before flush:%s
    ", buf);
        fflush(fp);
        printf("after flush:%s
    ", buf);
        printf("len of string in buf = %ld
    ", (long) strlen(buf));
    
        memset(buf, 'b', BSZ-2);
        buf[BSZ-2] = '';
        buf[BSZ-1] = 'X';
        fprintf(fp, "hello, world");
        fseek(fp, 0, SEEK_SET);
        printf("after fseek:%s
    ", buf);
        printf("len of string in buf = %ld
    ", (long) strlen(buf));
    
        memset(buf, 'c', BSZ-2);
        buf[BSZ-2] = '';
        buf[BSZ-1] = 'X';
        fprintf(fp, "hello, world");
        fclose(fp);
        printf("after fclose:%s
    ", buf);
        printf("len of string in buf = %ld
    ", (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前才有效。使用内存流相比临时文件,会有很大的性能提升。

     

  • 相关阅读:
    My97DatePicker使用说明文档
    内存溢出之Tomcat内存配置
    myeclipse控制台不显示tomcat信息
    修改 MyEclipse 编辑区域背景颜色
    window.open() 弹出窗体居中
    javascript控制页面控件隐藏显示的两种方法
    (转)MyEclipse7.5.0版注册码破解及激活操作
    [置顶] Android代码读取 android 设备的电池信息
    [置顶] Android代码获得我们手机的cpu序列号
    [置顶] Android高手进阶教程Android常用名令集锦(图文并茂)!
  • 原文地址:https://www.cnblogs.com/cauchy007/p/5642122.html
Copyright © 2020-2023  润新知