APUE(5)---標準I/O庫 (3)

10、定位流數組

#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);

11、格式化I/O
  此章節太過繁瑣,而且意義不大,之後須要使用再過補充。函數

12、實現細節性能

#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流打印緩衝狀態信息spa

十3、臨時文件指針

  ISO C標準I/O庫提供了兩個函數幫助建立臨時文件rest

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

  tmpnam函數產生一個與現有文件名不一樣的一個有效路徑名字符串,若ptr是NULL,則產生的路徑存放在一個靜態區,指向該靜態區的指針做爲函數值返回,後續調用tmpnam時,會重寫該靜態區;若ptr不是NULL,則認爲他應該是長度至少是L_tmpnam的字符數組,用這個名字建立臨時文件可能存在問題,由於獲得文件名和建立是兩個動做,不具備惟一性。tmpfile是直接建立一個臨時文件,其操做手法是先調用tmpnam產生一個惟一的路徑名,而後建立一個文件,而後當即unlink它。code

#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。blog

#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函數試圖修改字符串時,出現了段錯誤

十4、內存流
  標準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前纔有效。使用內存流相比臨時文件,會有很大的性能提高。

相關文章
相關標籤/搜索