zoukankan      html  css  js  c++  java
  • linux 之文件基础 (四)、标准IO的API

    1. 刷新缓冲区

    #include <stdio.h>
    int fflush(FILE *stream);
    

    2. 打开一个流

    #include <stdio.h>
    FILE *fopen(const char *pathname, const char *mode);
    FILE *fdopen(int fd, const char *mode);
    FILE *freopen(const char *pathname, const char *mode, FILE *stream);
    

    2.1 fopen

    #include <stdio.h>
    FILE *fopen(const char *pathname, const char *mode);
    FILE *fdopen(int fd, const char *mode);
    FILE *freopen(const char *pathname, const char *mode, FILE *stream);
    

    函数功能: 打开一个流。打开文件的权限为0666。

    2.1.1 函数参数

    (1)path : 路径名
    (2)mode : 打开方式,有以下可选项

    • r :只读方式。文件必须存在,否则报错。
    • r+ :读写方式。文件必须存在,否则报错。
    • w :只写方式。文件存在的话清空,不存在的话创建。
    • w+ :读写方式。文件存在的话清空,不存在的话创建。
    • a :以附加的方式打开(只写)。
    • a+ :以附加方式打开(读写)。

    在这里插入图片描述

    2.1.2 函数返回值

    • 成功:返回 指向FILE的结构体指针(流指针)
    • 失败:返回 NULL

    2.2 freopen

    函数功能:重定向输入输出流。

    2.2.1 函数参数

    (1)pathname
    重新定向的文件或者是路径
    (2) type
    文件打开的方式
    (3) fp
    被改变的流

    2.2.2函数返回值

    返回一个新的FILE指针。出错返回NULL。

    举栗子:
    freopen(“file1”, “w+”, stdout); 将stdout 重定向到 file1中。
    freopen("/dev/tty", “w+”, stdout); 将stdout 重定向到 当前进程的控制终端中。

    2.3 fdopen

    2.3.1 函数功能及函数原型

    FILE *fdopen(int fd, const char *mode);
    

    将一个文件描述符和一个流结合。一般的fopen 函数只能操作普通文件,而如果想要使用标准IO操作设备文件,则需要使用这个函数。
    一般使用场景:如果想用标准IO 操作设备文件,那么可以使用这个函数。即:使用标准IO 来操作设备文件。先获取设备文件的文件描述符,再使用标准IO操作。

    2.3.2 函数参数

    (1)fd
    要操作设备的文件描述符。
    (2)mode
    与前面的mode一样

    2.3.3 函数返回值

    返回一个新的FILE指针。出错返回NULL。

    3. 关闭一个流

    3.1 fclose 函数

    #include <stdio.h>
     int fclose(FILE *stream);
    

    功能:关闭一个已经打开的流。

    注意

    • 关闭一个流之前,会刷下流的缓冲区中的数据,并释放缓冲区资源(也就是它底层调用了 free() 函数)。(因为缓冲区是malloc 来的。)
    • 当一个进程终止,或者从main return 时,则所有未写缓冲区的流被刷新,所有打开的流都被关闭。
    • 关闭一个流之后,不能再次关闭,否则结果是未知的(类似于两次 free)。

    3.1.1 函数参数

    要关闭流的指针。

    3.1.2 函数返回值

    • 关闭成功返回0
    • 关闭错误返回EOF 并且设置错误码

    4. 从一个流读一个字符

    #include <stdio.h>
    int getc(FILE *stream);
    int fgetc(FILE *stream);
    int getchar(void);
    

    注意:

    • getchar、getc 是通过宏实现的。fgetc是通过函数实现的。
    • getchar() 等价于getc(stdin)
    • getc() fgetc() 使用方法和效果是一样的,只是内部的实现细节不同。

    4.1 函数参数

    • c 输出字符的 ASCII 码
    • stream 指定的流

    4.2 函数返回值

    • 读成功,获取读到字节的ASSII码
    • 读失败,返回EOF,并设置error

    5. 向一个流写一个字符

    #include <stdio.h>
    int putc(int c, FILE *stream);  
    int fputc(int c, FILE *stream);
    int putchar(int c);
    

    功能: 将指定的字符写入到指定的流中
    注意:

    • 从流中读取数据和向流中写数据三个函数一一对应。
    • fput 的实现方式是函数。putc 和 putchar 的实现方式是宏。
    • putc() fputc() 使用方法和效果是一样的,只是内部的实现细节不同。

    5.1 函数参数

    • c 输出字符的 ASCII 码
    • stream 指定额流

    5.2 函数返回值

    • 写成功,获取读到字节的ASSII码
    • 写失败,返回EOF,并设置error

    5.3 区分EOF

    5.3.1 区分EOF

    读写一个流时,读写失败和到达文件末尾都会返回EOF,那么我们如何区别由于什么原因返回的EOF呢?
    在FILE 结构体中,一般有两个标志字段,

    • 出错标志
    • 文件末尾标志

    通过这两个标志,可以区别区别由于什么原因返回的EOF。

    5.3.2 获取两种标志的函数

    #include <stdio.h>
    void clearerr(FILE *stream);
    int feof(FILE *stream);
    int ferror(FILE *stream);
    

    5.3.2.1 函数描述

    • feof : 用于检测文件末尾标志,如果该标志被设置返回非0的值,如果没有被设置返回0
    • ferror :用于检测出错标志,如果该标志被设置返回非0的值,如果没有设置返回0
    • clearerr :用于清除这两个标志

    5.3.2.2用法

    while (!feof(fp) && !ferror(stdin)) {
    	cTemp = fgetc(fp);
    } 
    

    解释: 如果没有达到文件末尾,以及没有出错的话,就继续读文件。也就是到达文件末尾以及出错了,都不能再继续读下去。

    6. 从流中读一行

    char *fgets(char *s, int size, FILE *stream);
    char *gets(char *s);
    

    推荐使用fgets。

    6.1 函数参数

    (1)s:

    • 用于存储从制定流的缓冲区中获取的数据的首地址。 这里的缓冲区指的是应用程序的缓冲区,不要和流的缓冲区弄混了。数据会先存到流的缓冲区里,再从流的缓冲区中搬运到应用程序的缓冲区里。

    (2)size:

    • 缓冲区的长度

    (3)stream:

    • 指定的流

    注意

    • 必须指定缓存的长度n。此函数一直读到下一个新行符为止,但是不超过n-1个字符,读入的字符被送入缓存。该缓存以null字符结尾。
    • 如若该行,包括最后一个新行符的字符数超过n-1,则只返回一个不完整的行,而且缓存总是以null字符结尾。对fgets()的下一次调用会继续读该行。
    • gets()并不将换行符存入缓存中, gets() 不把换行符加到新的缓冲区中。
    • gets()不能从指定的流中获取数据,只能从标准输入流中获取数据。

    比如:

    • 通过fgets() 由标准输入读取数据,输入1234 然后敲回车,fgets() 函数会将1234和回车都存到缓冲区中。
    • 通过gets() 由标准输入读取数据,输入1234 然后敲回车,gets() 函数只会将1234存到缓冲区中,而不会存回车。

    6.2 函数返回值

    • 读取成功:返回0
    • 读取失败,或者到达文件末尾 返回NULL

    7. 向流输出一行

    int puts(const char *s);
    int fputs(const char *s, FILE *stream);
    

    puts函数功能:
    向标准输出输出一行
    fputs函数功能:
    向指定的流里输出一行

    函数fputs()将一个以null符终止的字符串写到指定的流,终止符null不写出。
    注意

    • 这并不一定是每次输出一行,因为它并不要求在null符之前一定是新换行符。
    • puts()将一个以null符终止的字符串写到标准输出,终止符不写出。但是,puts()会自动将一个新换行符写到标准输出。

    puts 和fputs 区别演示

    #include <stdio.h>                                                              
    int main()
    {
        char buf[] = {'a','b','c','d','','e','f'};
     // fputs(buf,stdout);
        puts(buf);    
        return 0;
    }
    

    在这里插入图片描述可以看出:

    • fputs 读到 后不再向下读 并将读到的数据输出
    • puts 读到 后不再向下读,将读入的数据加上一个 后输出
    • 同时也说明fputs、puts并不一定是每次输出一行,因为它并不要求在null符之前一定是新换行符,也就是它读到一行的 后结束读。

    8. 二进制流

    疑问:

    fputs 和puts 只能读字符串类型 ,且读到 就不能继续向下读了。如果想要输出一个结构怎么办?使用fread fwrite 函数。这两个函数常用于从二进制文件中读或写一个结构。

    使用二进制读写的情况:

    • 两个函数的返回:读或写的对象数
    • 对于二进制数据我们更愿意一次读或写整个结构。
    • 为了使用getc()或putc()做到这一点,必须循环读取整个结构,一次读或写一个字节。(效率低)
    • fputs()在遇到null字节时就停止,而在结构中可能含有null字节,所以不能使用每次一行函数实现这种要求。如果输入数据中包含有null字节或换行符,则fgets()也不能正确工作。(实现限制)

    8.1 读写一个二进制文件流

    #include <stdio.h>
    size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); // 从一个二进制流读
    size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);// 向一个二进制流写
    

    8.1.1 函数参数:

    • ptr: 缓冲区的首地址
    • stream : 流
    • nmemb :元素个数(人为指定的)
    • size : 每个元素的大小(人为指定)

    8.1.2 返回值:

    • 成功 :实际读到元素的个数(凑不足一个元素就不算)
    • 失败 :-1

    8.1.3 使用举栗子

    9. 流的格式化输入输出

    9.1 由一个流格式化输出

    int printf(const char *format, ...); //stdout
    int fprintf(FILE *stream, const char *format, ...); //stream
    int sprintf(char *str, const char *format, ...); //char *
    
    • fprintf 和printf 的使用方法类似,只是printf 只能向stdout 流输出,但是fprintf 不仅可以向stdout输出,也可以向别的流中输入。
    • 我们可以通过sprintf 把我们想要的数据写到字符串中。

    9.2 向一个流格式化输入

    #include <stdio.h>
    int scanf(const char *format, ...); //stdin
    int fscanf(FILE *stream, const char *format, ...);//stream 流的格式化输入
    int sscanf(const char *str, const char *format, ...);//char *
    
    • scanf 只能从标准输入中获取数据。
    • fscanf 从指定的流中获取数据。
    • ssanf 从字符串中获取数据。

    9.3 举栗子

    9.3.1 fprintf

    打开一个流,采用流的格式化输出函数,向流中输入 指定的字符

    #include <stdio.h>
    int main()
    {                                                                               
        // step1 读写方式打开一个流
        FILE* filePtr = fopen("file.txt","r+");   
        int a = 10; 
        fprintf(filePtr,"test %d",a);
        return 0;
    } 
    

    9.3.2 sprintf

    将数据格式化输入到字符数组中。

    #include <stdio.h>
    int main()
    {
        char buf[10] = "";
        int a = 10; 
        sprintf(buf,"test %d",a);
        printf("%s",buf);
        return 0;
    }   
    

    9.3.3 fscanf

    打开一个流,从流中格式化读入数据,输出到stdout。

    #include <stdio.h>                                                              
    int main()
    {
        FILE* filePtr = fopen("file.txt","r+");
        char my_char[10]= "";
        int x = 0;
        fscanf(filePtr,"%s%d",my_char,&x);
        printf("%s %d",my_char,x);
        return 0;
    }
    

    9.4 sscanf

    从一个字符中格式化读入数据,输出到stdout。

    #include <stdio.h>                                                              
    int main()
    {
        char my_char2[10] = "sss 10"; 
        char my_char[10] = "";
        int x = 0;
        sscanf(my_char2,"%s%d",my_char,&x);
        printf("%s %d",my_char,x);
        return 0;
    }
    

    10. 流的定位

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

    10.1 函数参数

    (1)stream
    打开的流
    (2)offset
    相对于基准点偏移量
    (3)whence : 基准点

    • SEEK_SET:当前位置为文件的开头,新位置为偏移量的大小。
    • SEEK_CUR:当前位置为文件指针的位置,新位置为当前位置加上偏移量。
    • SEEK_END:当前位置为文件的结尾,新位置为文件的大小加上偏移量的大小。

    10.2 返回值

    • 成功 返回0
    • 失败 返回-1

    10.3 获取偏移量

    //返回当前流的偏移量. 等价于文件IO中 lseek 函数的返回值
    long ftell(FILE *stream);
    //rewind()等价于(void)fseek(stream, 0L, SEEK_SET)
    void rewind(FILE *stream);
    

    11. 获取系统时间函数

    11.1 time函数

    #include <time.h>
    time_t time(time_t *t);  time_t == long 
    

    函数功能:

    • 返回从1970-1-1 0:0:0到函数调用那一刻的秒数(unix 诞生的时间)

    函数参数

    • t: 获取后的时间存储的位置。即 如果t非NULL,就将时间填入到t所指向的内存空间

    返回值:

    • 成功 : 时间
    • 失败 :((time_t) -1) 即将 -1 强转成time_t 返回

    11.2 localtime 函数

    #include <time.h>
    struct tm *localtime(const time_t *timep);
    

    函数功能:

    • 将秒数转换成具体的时间

    函数参数:

    • timep : 存秒数的地址

    返回值:
    tm: 结构指针,结构为:

    struct tm {
    	int tm_sec;    /* Seconds (0-60) */
    	int tm_min;    /* Minutes (0-59) */
    	int tm_hour;   /* Hours (0-23) */
    	int tm_mday;   /* Day of the month (1-31) */
    	int tm_mon;    /* Month (0-11) */
    	int tm_year;   /* Year - 1900 */
    	int tm_wday;   /* Day of the week (0-6, Sunday = 0) */
    	int tm_yday;   /* Day in the year (0-365, 1 Jan = 0) */
    	int tm_isdst;  /* Daylight saving time */
    };
    

    12. 标准IO 和文件IO 的总结

    12.1 文件I/O和标准I/O区别

    • 文件I/O是低级I/O,支持POSIX规范,任何支持POSIX标准的操作系统都支持使用文件I/O。(类
      UNIX)标准I/O是高级I/O,支持ANSI C相关标准,只要开发环境支持标准C库,标准I/O就可以使用。
    • 通过文件I/O读取文件时,每次操作都会执行相应的系统调用。好处是直接读取实际文件,坏处是频繁调用系统调用,增加系统开销,效率低。标准I/O可以看成在文件I/O的基础之上封装了缓冲机制,先读写缓冲区,必要时读写真实文件,从而减少系统调用的次数。
    • 文件I/O使用文件描述符,表示一个打开的文件,可以访问不同类型的文件,普通文件,设备文件和管道文件。标准I/O中用FILE(流)来表示一个打开的文件,通常只用来访问普通文件。(如果想要使用标准IO 操作 非普通文件,可以使用fdopen函数。)

    12.2 getchar putchar

    • 当程序调用getchar时.程序就等着用户按键.用户输入的字符被存放在键盘缓冲区中。直到用户按回车为止(回车字符也放在缓冲区中)。
    • 当用户键入回车之后,getchar才开始从stdio流中每次读入一个字符。
    • getchar函数的返回值是用户输入的字符的ASCII码,如出错返回-1,且将用户输入的字符回显到屏幕。
    • 如用户在按回车之前输入了不止一个字符,其他字符会保留在键盘缓存区中,等待后续getchar调用读取。
    • 也就是说,后续的getchar调用不会等待用户按键,而直接读取缓冲区中的字符,直到缓冲区中的字符读完为后,才等待用户按键。

    13. 举栗子

    13.1 获取一个文件的行数,文件由命令行参数指定

    #include <stdio.h>
    #include <string.h>
    /*
     *获取一个文件的行数,文件由命令行参数指定                                                                                                  
     * */
    int main(int argc, const char *argv[])
    {
        if(argc != 2)
        {   
            perror("参数不准确");
            return -1; 
        }   
    
        FILE * filePtr;
        // step1 打开流
        if((filePtr = fopen(argv[1],"r")) == NULL)
        {   
            perror("open file error");
            return -1; 
        }   
        char read_buf[1024];
        int line = 0;
        // 如果fgets 不等NULL 代表没有出错且没有到达文件尾 
        while(fgets(read_buf,sizeof(read_buf),filePtr) != NULL)
        {   
            //因为一次fgets 不一定读到完整的一行数据,如果一行没有读完,那么line不应该自加。 只有读到了
     才代表一行读完了。
            //而 read_buf 的最后一个字节自动设置为  因此倒数第2个字节应该存的是
     否则就没有读完一行 
            if(read_buf[strlen(read_buf)-1] == '
    ');
                ++line;
        }   
        printf("文件共有%d 行
    ",line);
        return 0;
    }
    

    13.2 转换字母大小写

    #include <stdio.h>                                                                                                                             
    int main()
    {
        int read_char;
        while((read_char = fgetc(stdin)) != EOF)
        {   
            fputc(read_char,stdin);
            if('a' <= read_char && read_char <= 'z')
            {
                read_char = read_char - 32; 
            }
            else if('A' <= read_char && read_char <= 'Z')
            {
                read_char = read_char + 32; 
            }
            fputc(read_char,stdin);
            printf("%c",read_char);
            if(read_char == '
    ')
                break;
        }   
        return 0;
    }
    
  • 相关阅读:
    347. Top K Frequent Elements
    437. Path Sum III
    338. Counting Bits
    337. House Robber III
    494. Target Sum
    416. Partition Equal Subset Sum
    LINUX 使用grep命令查看某个指定时间段的日志
    git 常用命令操作
    Python之IDE工具下载安装及注册详解及创建项目
    Python下载安装及验证
  • 原文地址:https://www.cnblogs.com/lasnitch/p/12764119.html
Copyright © 2011-2022 走看看