zoukankan      html  css  js  c++  java
  • Linux I/O总结

    文件流

    标准I/O文件流可用于单字节或多字节字符集。流的定向决定了所读写的是单字节还是多字节。流在最初创建时,并没有定向,此时如果在为定向的流上使用多字节I/O函数,那么该流被设置为宽定向的;如果在为定向的流中使用单字节I/O函数,那么该流被设置为字节定向的。

    如下两个函数可用于改变流的定向:

    #include <stdio.h>

    #include <wchar.h>

    int fwide(FILE* fp, int mode);

    返回值:流为宽定向,返回正值;流为字节定向,返回负值;流为定向,返回0

    说明:

    fwide不改变已定向的流的定向。

    mode > 0,则fwide试图将流设置为宽定向;

    mode < 0,则fwide试图将流设置为字节定向;

    mode = 0,则fwide不设置流的定向,而是返回标识当前流定向的值。

     

    FILE* freopen(const char* restrict pathname, const char* restrict type, FILE* restrict fp);

    说明:

    在指定的流上打开一个指定的文件,如果该流已经打开,则先关闭该流;若该流已经定向,则清除该定向。该函数常用于将一个指定的文件打开为一个预定义的流:标准输入、标准输出、标准错误

    缓冲

    标准I/O提供缓冲的目的是尽可肯地减少read和write的次数,有3种类型:

    类型

    说明

    全缓冲

    在填满整个缓冲区后才进行实际的I/O操作。可以调用fflush函数将缓冲区的数据写入磁盘中。

    行缓冲

    当在输入和输出中遇到换行符或者缓冲区已满时,执行实际的I/O操作。

    无缓冲

    标准I/O不对字符进行缓冲存储。标准错误流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);

    返回值:成功,0;失败,-1

    说明:

    setbuf用于打开或关闭流缓冲机制,参数buf指向一个长度为BUFSIZ(该常量在<stdio.h>中定义)的缓冲区;如果要关闭缓冲,则将buf设置为NULL即可。

    setvbuf用于精确地设置所需的缓冲类型,mode取值如下:_IOFBF(全缓冲)/_IOLBF(行缓冲)/_IONBF(无缓冲);如果指定了mode为带缓冲类型,而buf却为NULL,则系统会自动分配BUFSIZ个字节的缓冲区。

     

    int fflush(FILE* fp);

    说明:

    强制冲刷一个流到磁盘中。如果fp为NULL,则系统中的所有输出流将被冲刷。

    打开流

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

    说明:

    fopen打开指定路径pathname的文件;freopen用于将一个指定文件打开为一个预定义的流:标准输入、标准输出、标准错误;fdopen根据fd返回一个打开的流,常用于由创建管道和网络通信函数返回的描述符。

    type参数指定了对该I/O流的读写方式。

     

    读写流

    在打开流后,有3种不同类型的非格式化I/O(格式化I/O是诸如printf和scanf等的函数):

    类型

    说明

    每次处理一个字符

    #include <stdio.h>

    int getc(FILE* fp);

    int fgetc(FILE* fp);

    int getchar(void);

    返回值:成功,返回下一个字符;失败或到达文件尾端,返回EOF

    说明:

    getchar等价于getc(stdin)。

    getc可以被实现为宏,而fgetc一定是个函数,因此fgetc调用的时间通常长于getc。

     

    为了区分引起EOF的原因是到达文件尾端还是读入失败,引入如下三个函数:

    int ferror(FILE* fp);

    int feof(FILE* fp);

    void clearerr(FILE* fp);

    返回值:条件为真,非0;否则,0

    说明:

    每个流在FILE对象中维护了两个标志:出错标志、文件结束标志,调用clearerr可以清除这两个标志。

     

    有时在读一个输入流时,我们需要先查看下一个字符是否是需要读入的字符,此时需要利用ungetc函数将字符压回输入流中:

    int ungetc(int c, FILE* fp);

    返回值:成功,返回c;失败,EOF

    说明:

    用ungetc压回字符,并非将其写到底层文件或设备中,只是将其写回标准I/O的缓冲区中。

     

    int putc(int c, FILE* fp);

    int fputc(int c, FILE* fp);

    int putchar(int c);

    返回值:成功,返回c;失败,EOF

    说明:

    putchar(c)等价于putc(c, stdout)。

    putc可被实现为宏,而fputc只能是一个函数。

     

    每次处理一行

    #include <stdio.h>

    char* fgets(char* restrict buf, int n, FILE* restrict fp);

    char* gets(char* buf);

    返回值:成功,返回buf;失败,返回NULL

    说明:

    gets从标准输入读,fgets从指定的文件读。

    每次读取直至遇到换行符或者达到缓冲区长度n(由于缓冲区以null字节结尾,有效长度实则是n - 1),如果fgets读取的行超过n – 1个字节,那么返回前n – 1个字节,对fgets的下一次调用将继续读取该行其余部分。

    gets由于不能指定缓冲区长度n,因此在最新的ISO C中已被忽略,因此不推荐使用gets函数。

     

    int fputs(const char* restrict str, FILE* restrict fp);

    int puts(const char* str);

    返回值:成功,返回非负值;失败,返回EOF

    说明:

    fputs将一个以null字节终止的字符串写到指定的流中,null终止符不写出。

    虽然,puts并没有太大的安全隐患,但还是避免使用它,因为puts每次写出数据后还附加写出一个换行符。

    直接I/O或二进制I/O

    比如我们需要一次读写一个完整的结构,如果使用getc/putc,那么必须循环遍历整个结构,每次处理一个字节,非常麻烦;如果使用fgets/fputs,那么由于其遇到null字节就停止了,而在结构中可能含有null字节,因此也不能很好地工作。因此,提供了如下两个函数:

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

    返回值:成功读写的结构个数

    例如:将data数组的第2~5个元素(一共4个)写至一个文件上:

    float data[10];

    if(fwrite(&data[2], sizeof(float), 4, fp) != 4)

        printf(“fwrite error”);

    示例程序:将标准输入复制到标准输出:
    1)每次处理一个字符(getc/putc)
    [root@benxintuzi IO]# cat getc.c
    #include <stdio.h>
    
    int main(void)
    {
            int c;
    
            while((c = getc(stdin)) != EOF)
                    if(putc(c, stdout) == EOF)
                            printf("output error
    ");
    
            if(ferror(stdin))
                    printf("input error
    ");
    
            return 0;
    }
    
    [root@benxintuzi IO]# ./getc
    1 hello
    1 hello
    2 benxintuzi
    2 benxintuzi
    ^C
    [root@benxintuzi IO]#
    
    (2) 每次处理一行(fgets/fputs)
    [root@benxintuzi IO]# cat fgets.c
    #include <stdio.h>
    
    #define MAXLINE 4096
    
    int main(void)
    {
            char buf[MAXLINE];
    
            while(fgets(buf, MAXLINE, stdin) != NULL)
                    if(fputs(buf, stdout) == EOF)
                            printf("output error
    ");
    
            if(ferror(stdin))
                    printf("input error
    ");
    
            return 0;
    }
    
    [root@benxintuzi IO]# ./fgets
    benxin
    benxin
    tuzi
    tuzi
    ^C
    [root@benxintuzi IO]#

    流定位

    #include <stdio.h>

    long ftell(FILE* fp);

    offset ftello(FILE* fp);

    返回值:成功,返回文件当前位置;失败,返回-1L

     

    int fseek(FILE* fp, long offset, int whence);

    int fseeko(FILE* fp, off_t offset, int whence);

    返回值:成功,0;失败,-1

    说明:

    whence取值如下:SEEK_SET/SEEK_CUR/SEEK_END。

    fseek和fseeko唯一的区别是offset的类型不同。

     

    void rewind(FILE* fp)将一个流设置到文件的起始位置。

     

    int fgetpos(FILE* restrict fp, fpos_t* restrict pos);

    int fsetpos(FILE* fp, const fpos_t* pos);

    返回值:成功,0;失败,-1

    说明:

    fgetpos将当前文件指针存入pos中;fsetpos将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写到标准输出,fprintf写到文件,dprintf写到文件描述符,sprintf写到buf中,但是可能会溢出,snprintf也写到buf中,但是由于指定了缓冲区长度n,可能截断但不会溢出。

     

    格式化输入:

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

     

    格式控制:

    %[flags][fldwidth][precision][lenmodifier]convtype

    flags

    说明

    '

    将整数按千位分组输出

    -

    左对齐输出

    +

    显示带符号转换的正负号

    空格

    如果第一个字符不是正负号,则用空格代替

    #

    指定转换格式,例如0x前缀

    0

    用0而非空格进行填充

    fldwidth最小字段宽度。若转换后字符数小于该值,则用空格填充。该值可以是一个非负整数或*。

    precision精度表示,整数位数、浮点数小数位数、字符串最大字节数。该值可以是一个.加上非负整数或者*。

    lenmodifier参数长度。hh(signed或unsigned char)/h(signed或unsigned short)/l(signed或unsigned long)/ll(signed或unsigned long long)/j(intmax_t或uintmax_t)/z(size_t)/t(ptrdiff_t)/L(long double)。

    convtype转换类型:

    d、i

    有符号十进制

    o

    无符号八进制

    u

    无符号十进制

    x、X

    无符号十六进制

    f、F

    双精度浮点数

    e、E

    指数格式双精度浮点数

    g、G

    根据转换后的值解释为f、F、e、E

    a、A

    十六进制指数格式双精度浮点数

    c

    字符

    s

    字符串

    C

    宽字符

    S

    宽字符串

    %

    %本身

    p

    void*指针

    临时文件

    每个标准I/O流都有一个相关联的文件描述符,可以调用int fileno(FILE* fp)来获得这个描述符。如下两个函数用于创建临时文件:

    #inlcude <stdio.h>

    char* tmpnam(char* ptr);

    FILE* tmpfile(void);

    说明:

    tmpnam产生一个与现有文件名不同的临时文件名,每次调用时,都产生一个不同的临时文件名。最多的调用次数为TMP_MAX(定义在<stdio.h>中)。若ptr为NULL,则所产生的临时文件名存放在一个静态区中,指向该静态区的指针作为函数值返回;如果ptr不为NULL,则其指向长度大于等于L_tmpnam个字符的数组,所产生的临时文件名存放在该数组中,ptr作为函数值返回。

    tmpfile创建一个临时的二进制文件(wb+),在关闭文件或程序结束时自动删除该文件。

     

    #include <stdlib.h>

    char* mkdtemp(char* template);

    int mkstemp(char* template);

    说明:

    mkdtemp创建一个目录,返回指向目录名的指针;mkstemp创建一个文件,返回文件描述符。

    template的后六位设置为XXXXXX。函数将这些占位符替换成不同的字符来构建一个唯一的名称。

    [root@benxintuzi IO]# cat tmp.c
    #include <stdio.h>
    
    int main(void)
    {
            char name[L_tmpnam], line[4096];
            FILE* fp;
    
            printf("%s
    ", tmpnam(NULL));                   /* first temp name */
    
            tmpnam(name);                                   /* second temp name */
            printf("%s
    ", name);
    
            if((fp = tmpfile()) == NULL)                    /* create temp file */
                    printf("tmpfile error
    ");
            fputs("write one line to tmpfile
    ", fp);       /* write to temp file */
    
            rewind(fp);                                     /* then read it back */
            if(fgets(line, sizeof(line), fp) == NULL)
                    printf("fgets error
    ");
            fputs(line, stdout);                            /* print the line */
    
            return 0;
    }
    [root@benxintuzi IO]# gcc tmp.c -o tmp
    /tmp/cc6sXVXs.o: In function `main':
    tmp.c:(.text+0x14): warning: the use of `tmpnam' is dangerous, better use `mkstemp'
    [root@benxintuzi IO]# ./tmp
    /tmp/filekyJuQu
    /tmp/fileKA5UyL
    write one line to tmpfile
    [root@benxintuzi IO]#
    [root@benxintuzi IO]# cat mkstemp.c
    #include <errno.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/stat.h>
    
    void make_temp(char *template);
    
    int main(void)
    {
            char    good_template[] = "/tmp/dirXXXXXX";     /* right way */
            char    *bad_template = "/tmp/dirXXXXXX";       /* wrong way*/
    
            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     sbuf;
    
            if ((fd = mkstemp(template)) < 0)
                    printf("can't create temp file
    ");
            printf("temp name = %s
    ", template);
            close(fd);
            if (stat(template, &sbuf) < 0) {
                    if (errno == ENOENT)
                            printf("file doesn't exist
    ");
                    else
                            printf("stat failed
    ");
            } else {
                    printf("file exists
    ");
                    unlink(template);
            }
    }
    
    [root@benxintuzi IO]# gcc mkstemp.c -o mkstemp
    [root@benxintuzi IO]# ./mkstemp
    trying to create first temp file...
    temp name = /tmp/dirnemrcT
    file exists
    trying to create second temp file...
    Segmentation fault (core dumped)
    [root@benxintuzi IO]#
    
    说明:
    对于第一个模板,由于使用了数组,则数组内容存储在栈上;但是第二个模板,只有指针本身存储在栈上,而具体字符串内容却存放在可执行文件的只读段中,因此当mkstemp函数试图修改字符串时,出现segment fault。

    内存流

    在内存流中,所有的I/O都是通过在缓冲区与主存之间来回传送字节来完成的。由于避免了缓冲区溢出,因此内存流非常适用于创建字符串。内存流只访问主存,不访问磁盘上的文件,所以性能方面会有显著的提升。有三个函数用于内存流的创建:

    #include <stdio.h>

    FILE* fmemopen(void* restrict buf, size_t size, const char* restrict type);

    返回值:成功,返回流指针;失败,返回NULL

    说明:

    fmemopen函数允许调用者指定自己的缓冲区用于内存流。buf指向缓冲区的开始位置,size指定了缓冲区的大小,type参数控制流的使用方式:r/rb/w/wb/a/ab/r+/r+b/rb+/w+/w+b/wb+/a+/a+b/ab+

    注意点:

    (1)以追加方式打开内存流时,当前文件位置设为缓冲区中的第一个null字节;如果缓冲区中不存在null字节,则当前文件位置设为缓冲区结尾的后一个字节。

    (2)以其他方式打开内存流时,当前文件位置设为缓冲区的开始位置。

    (3)如果buf为null,则打开内存流没有任何意义。

    (4)增加流缓冲区中的数据或者调用fclose、fflush、fseek、fseeko、fsetpos时都会在当前位置写入一个null字节。

     

    #include <stdio.h>

    FILE* open_menstream(char** bufp, size_t* sizep);

    #include <wchar.h>

    FILE* open_wmemstream(wchar_t** bufp, size_t sizep);

    返回值:成功,返回流指针;失败,返回NULL

    说明:

    open_memstream函数创建的流是面向字节的,其与fmemopen的区别如下:

    创建的流只能写打开;

    不能指定自己的缓冲区,可以通过bufp和sizep访问缓冲区地址和大小;

    关闭流后需要自行释放缓冲区;

    对流添加字节会增加缓冲区的大小。

     

    以下程序说明了如何在我们自己提供的缓冲区上操作内存流:

    [root@benxintuzi IO]# cat memstr.c
    #include <stdio.h>
    #include <stdlib.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)
                    printf("fmemopen failed
    ");
            printf("initial buffer contents: %s
    ", buf);
            fprintf(fp, "hello, world");
            printf("before flush: %s
    ", buf);
            fflush(fp);
            printf("after fflush: %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);
    }
    
    [root@benxintuzi IO]# ./memstr
                                                # 用a字符改写缓冲区
    initial buffer contents:                                          # fmemopen在缓冲区开始处放置null字节
    before flush:                                                            # 流冲刷后缓冲区才会变化
    after fflush: hello, world
    len of string in buf = 12                                              # null字节加到字符串结尾
                                                # 现在用b字符改写缓冲区
    after  fseek: bbbbbbbbbbbbhello, world                                  # fseek引起缓冲区冲刷
    len of string in buf = 24                               # 再次追加写null字节
    after fclose: hello, worldcccccccccccccccccccccccccccccccccc          # 现在用c字符改写缓冲区
    len of string in buf = 46                                                # 没有追加写null字节
    [root@benxintuzi IO]#
  • 相关阅读:
    费用流入门
    网络最大流入门
    假期编程
    假期编程
    假期编程
    假期编程
    假期编程
    假期编程
    假期编程
    假期编程
  • 原文地址:https://www.cnblogs.com/benxintuzi/p/4763919.html
Copyright © 2011-2022 走看看