• 【文件I/O】标准IO:库函数


    标准I/O:库函数

    一、I/O相关知识

    1.1最早接触的I/O

    #include<stdio.h> 标准的输入输出头文件
    它是printf、scanf函数的头文件,它们都是标准IO

    1.2I/O的种类

    ​标准IO:库函数
    文件IO:系统调用

    1.3库函数和系统调用

    系统调用:系统调用就是从用户空间进入内核空间的一次过程,系统调用没有缓冲区,系统调用的效率低,系统调用可移植性比较差。

    库函数:库函数=缓冲区+系统调用,库函数的效率比系统调用的高,库函数可移植比较强。

    1.4什么是FILE?

    FILE:又被称为流,是一个结构体类型,标准I/O用FILE来存放打开的文件的相关信息(缓冲区,系统调用相关的内容),标准I/O的所有操作都是围绕FILEl来进行的。在一个正在执行的程序中默认已经有三个FILE的指针stdin,stdout,stderr。

    标准I/O预定义3个流,程序运行时自动打开

    标准输入流0STDIN_FILENOstdin
    标准输出流1STDOUT_FILENOstdout
    标准错误流2STDERR_FILENOstderr

    FILE结构体

    typedef struct _IO_FILE FILE;
    
    struct _IO_FILE {
    	char* _IO_buf_base;	//缓冲区的起始地址
      	char* _IO_buf_end;	//缓冲区的结束地址
        int _fileno;        //系统调用的文件描述符
        ...
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    二进制流/文本流换行符

    Liniux
    换行符 <---> '\n'
    
    windows
    二进制流:换行符 <---> '\n'
    文本流:换行符 <---> '\r''\n'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    二、标准I/O函数

    1.fopen、fclose、strerror、perror(打开、关闭文件,输出错误码信息)

    fopen、fclose函数API

    #include 
    FILE *fopen(const char *pathname, const char *mode);
    功能:打开文件(标准IO)
    参数:
        @pathname:想要打开文件的路径及名字 "./hello.txt" "/home/linux/1.c"
     	@mode:打开文件的方式 "a" "a+" "w" "w+" "r" "r+"
       r      以只读的方式打开文件,将光标定位到文件的开头
       r+     以读写的方式打开文件,将光标定位到文件的开头
       w      以只写方式打开文件,如果文件存在就清空,如果文件不存在就创建,将光标定位到开头
       w+     以读写方式打开文件,如果文件存在就清空,如果文件不存在就创建,将光标定位到开头
       a      以追加的方式打开文件,如果文件不存在就创建,如果文件存在不会清空,将光标定位到结尾
       a+     以读和追加的方式打开文件,如果文件不存在就创建,读光标在开头,写光标在结尾(光标就一个)
    返回值:成功返回文件指针,失败返回NULL,置位错误码errno
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    int fclose(FILE *stream);
    功能:关闭文件
    参数:
        @stream:文件指针
     返回值:成功返回0,失败返回EOF(-1),置位错误码  //#define EOF (-1)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    errno(内核中错误码问题)

    #include 
    extern int errno;
    errno 存放错误码
    
    • 1
    • 2
    • 3

    当用户通过fopen调用系统接口的时候,会打开文件,如果文件打开失败会给fp设置为NULL,同时将errno设置为对应的错误码,在内核中一个有4K个错误码,通过错误码的数值表示错误的类型。(errno是在#include 的头文件中声明的)
    在这里插入图片描述

    strerror、perror函数API

    #include 
    char *strerror(int errnum);
    功能:将错误码转换为错误信息
    参数:
        @errnum:错误码
    返回值:错误信息的字符串
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    #include 
    void perror(const char *s);
    功能:打印错误信息
    参数:
        @s:用户的附加信息
    返回值:无
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    1.1fopen、fclose函数实例1

    #include 
    
    int main(int argc, const char *argv[])
    {
        // 1.定义文件流指针
        FILE *fp;
    
        // 2.以只写方式打开文件,若文件不存在,创建文件
        if (NULL == (fp = fopen("./1.txt", "w"))) {
            perror("fopen errror");
            return -1;
        }
    
        // 3.关闭文件流指针
        fclose(fp);
    
        puts("--------start");
        fclose(stdout);
        puts("--------end");  //关闭标准输出流stdout,终端不会打印这行
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    1.2strerror、perror函数实例2

    #include 
    #include 
    #include 
    
    int main(int argc, const char *argv[])
    {
        // 1.定义文件流指针
        FILE *fp;
    
        // 2.以只读方式打开文件,若文件不存在,打印错误信息
        if (NULL == (fp = fopen("./1.txt", "r"))) {
            printf("errno = %d, errmsg = %s\n", errno, strerror(errno));
            perror("fopen error");
            return -1;
        }
    
        // 3.关闭文件流指针
        fclose(fp);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    2.fgetc、fputc、getchar、putchar(读、写一个字符)

    fgetc、fputc函数API

    int fgetc(FILE *stream);
    功能:从文件中读取一个字符(光标自动向后移动)
    参数:
        @stream:文件指针
    返回值:成功返回读取到的字符的ASCII,读取到结尾或者遇到错误就返回EOF
    
    • 1
    • 2
    • 3
    • 4
    • 5
    int fputc(int c, FILE *stream);
    功能:向文件中写入一个字符(光标自动向后移动)
    参数:
        @c:字符的ascii的值 'c' 65
        @stream:文件指针
    返回值:成功返回写入到的字符的ASCII,失败返回EOF
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    getchar、putchar函数API

    int getchar(void);
    getchar()等同于fgetc(stdin)
    
    • 1
    • 2
    int putchar(int c);
    putchar(c)等同于fputc(ch, stdout)
    
    • 1
    • 2

    2.1fgetc函数实例1(从文件读)

    #include 
    
    int main(int argc, const char *argv[])
    {   
        FILE *fp;
        if (NULL == (fp = fopen("./1.txt", "r"))) {
            perror("fopen error");
            return -1;
        }
    
        // 文件内容 abcd
        char ch;
        while (EOF != (ch = fgetc(fp))) {
            printf("%c", ch);
        }
        
        fclose(fp);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    2.2fgetc函数实例2(从终端读)

    #include 
    
    int main(int argc, const char *argv[])
    {
        char ch;
        ch = fgetc(stdin);
        printf("ch = %c\n", ch);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2.3fgetc函数实例3(统计文件行号)

    #include 
    
    int main(int argc, const char *argv[])
    {   
        // 1.对输入参数的个数进行检查
        if (2 != argc) {
            fprintf(stderr, "Usage: %s \n", argv[0]);
            return -1;
        }
    
        // 2.以只读的方式打开文件
        FILE *fp;
        if (NULL == (fp = fopen(argv[1], "r"))) {
            perror("fopen error");
            return -1;
        }
    
        // 3.读取文件中的字符
        int line = 0;
        char ch;
        // 如果没有读取到文件的结尾,循环就继续,如果读取到文件的结尾那就停止
        while (EOF != (ch = fgetc(fp))) {
            if ('\n' == ch) {
                line++;
            }
        }
    
        // 4.打印行号,关闭文件
        printf("%s line = %d\n", argv[1], line);
        fclose(fp);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    2.4fputc函数实例4(向文件写)

    #include 
    
    int main(int argc, const char *argv[])
    {
        if (2 != argc) {
            fprintf(stderr, "Usage: %s \n", argv[0]);
            return -1;
        }
    
        FILE *fp;
        if (NULL == (fp = fopen(argv[1], "w"))) {
            perror("fopen error");
            return -1;
        }
    
        char ch = 'A';
        fputc(ch, fp);
    
        fputc('h', fp);
        fputc('e', fp);
        fputc('l', fp);
        fputc('l', fp);
        fputc('o', fp);
        fputc('!', fp);
    
        fclose(fp);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    2.5fputc函数实例5(向终端写)

    #include 
    
    int main(int argc, const char* argv[])
    {
        //向终端上写hello'\n'
        fputc('h', stdout);
        fputc('e', stdout);
        fputc('l', stdout);
        fputc('l', stdout);
        fputc('o', stdout);
        fputc('\n', stdout);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2.6fputc函数实例6(文件拷贝)

    #include 
    
    int main(int argc, const char *argv[])
    {   
        // 1.对输入参数的个数进行检查
        if (3 != argc) {
            fprintf(stderr, "Usage: %s  \n", argv[0]);
            return -1;
        }
    
        // 2.以只读的方式打开文件,以只写方式打开目标文件
        FILE *sfp, *dfp;
        if (NULL == (sfp = fopen(argv[1], "r"))) {
            perror("open src_file error");
            return -1;
        }
        if (NULL == (dfp = fopen(argv[2], "w"))) {
            perror("open dest_file error");
            return -1;
        }
    
        // 3.读取文件中的字符
        char ch;
        while (EOF != (ch = fgetc(sfp))) {
            fputc(ch, dfp);
        }
    
        // 4.关闭文件
        fclose(sfp);
        fclose(dfp);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    3.fgets、fputs、gets、puts(读、写一行字符)

    fgets、fputs函数API

    char *fgets(char *s, int size, FILE *stream);
    功能:从文件中读取字符串到向s指向的内存中,遇到'\n'或size-1个字符返回(最后一个用于存'\0')。
    	fgets遇到EOF或者换行符的时候就会停止,如果遇到换行符停止的,换行符也会被存储到s中。
    参数:
        @s:指向存储字符的首地址
        @size:想要读取字符的个数
        @stream:文件指针
    返回值:成功返回s,失败返回NULL
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    int fputs(const char *s, FILE *stream);
    功能:将s指向的内存中的字符串写入到文件中
    参数:
        @s:被写字符串的首地址
     	@steam:文件指针
    返回值:成功返回大于0的值,失败返回EOF
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    gets、puts函数API

    char *gets(char *s);
    功能:gets从标准输入stdin读字符串到s指向的内存
    返回值:成功返回时s,遇到'\n'或已输入size-1个字符时返回,总是包含'\0',到文件末尾或出错返回NULL
    gets不推荐使用,容易造成缓冲区溢出
    
    • 1
    • 2
    • 3
    • 4
    int puts(const char *s);
    功能:puts将缓冲区s中的字符串输出到stdout,并追加'\n'
    返回值:成功时返回输出的字符个数,失败返回EOF
    
    • 1
    • 2
    • 3

    3.1fgets函数实例1(从文件读)

    #include 
    
    int main(int argc, const char *argv[])
    {
        FILE *fp;
        if (NULL == (fp = fopen("./1.txt", "r"))) {
            perror("fopen error");
            return -1;
        }
     
        char buf[10] = {0};
        //fgets读取到换行符'\n'或sizeof(buf)-1个字符返回,将读取字符串最后一个字符buf[strlen(buf)]填充为'\0'
        while (NULL != fgets(buf, sizeof(buf), fp)) {
            printf("%s", buf);  //注意:在linux中创建的文本中最后末尾一行也有换行符'\n'
        }
    
        fclose(fp);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    3.2fgets函数实例2(从终端读)

    #include 
    #include 
    
    int main(int argc, const char* argv[])
    {
        char buf[10] = {0};
    
        //终端输入: hello\n
        //fgets读取到换行符'\n'或sizeof(buf)-1个字符返回,将读取字符串最后一个字符buf[strlen(buf)]填充为'\0'
        if (NULL == fgets(buf, sizeof(buf), stdin)) {
            printf("fgets error\n");
            return -1;
        }
    
        buf[strlen(buf) - 1] = '\0';  //将终端读取的'\n'设置为'\0'
        printf("buf = %s\n", buf);  //buf = hello,终端输出的换行符是printf里的
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3.3fgets函数实例3(统计文件行号)

    #include 
    #include 
    
    int main(int argc, const char *argv[])
    {
         // 1.对输入参数的个数进行检查
        if (2 != argc) {
            fprintf(stderr, "Usage: %s \n", argv[0]);
            return -1;
        }
    
        // 2.以只读的方式打开文件
        FILE *fp;
        if (NULL == (fp = fopen(argv[1], "r"))) {
            perror("fopen error");
            return -1;
        }
    
        // 3.读取文件中的字符
        char buf[10] = {0};
        int line = 0;
        //fgets读取到换行符'\n'或sizeof(buf)-1个字符返回,将读取字符串最后一个字符buf[strlen(buf)]填充为'\0'
        while (NULL != fgets(buf, sizeof(buf), fp)) {  
            if ('\n' == buf[strlen(buf) - 1]) {  //注意:在linux中创建的文本中最后末尾一行也有换行符'\n'
                line++;
            }
        }
    
        // 4.打印结果,关闭文件
        printf("%s line = %d\n", argv[1], line);
        fclose(fp);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    3.4fputs函数实例4(向文件写)

    #include 
    #include 
    
    int main(int argc, const char *argv[])
    {
        FILE *fp;
        if (NULL == (fp = fopen("./1.txt", "w"))) {
            perror("fopen error");
            return -1;
        }
    
        char buf[20] = "hello world!";
        fputs(buf, fp);
        fputs("xiao ming", fp);
    
        fclose(fp);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3.5fputs函数实例(向终端写)

    #include 
    
    int main(int argc, const char *argv[])
    {
        char buf[20] = "hello wrorld";
        fputs(buf, stdout);
        fputs("xiao ming\n", stdout);
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    4.fread、fwrite、feof、ferror(读、写若干个对象,判断文件是否结束或出错)

    fread、fwrite函数API
    即可读写文本文件,也可读写数据文件,效率高。

    size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
    功能:从stream中读取nmemb项数据,每一项的大小是size,将它们存到ptr。
    参数:
    	@ptr:存储读取到数据的首地址
     	@size:每一项的大小
    	@nmemb:项目的个数
        @stream:文件指针
    返回值:成功返回读取到的项目的个数,如果小于项目的个数就是错误或者到文件的结尾了。
    		fread读取到错误或文件结尾返回,不会给读取的字符串数据填充'\0'。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
    功能:将ptr中的数据写入到stream中,写nmemb向,每一项的大小是size
    参数:
    	@ptr:被写数据的首地址
    	@size:每一项的大小
    	@nmemb:项目的个数
    	@stream:文件指针
    返回值:成功返回项目的个数,失败返回小于项目的个数。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    feof、ferror函数API

    fread返回值无法区分是到达了文件的结尾还是错误发生了,调用者必须通过feof或者ferror函数来判断。
    int feof(FILE *stream);
    功能:判断是否读取到了文件的结尾,如果到了文件的结尾将返回真
    参数:
        @stream:文件指针
    返回值:如果到了文件的结尾返回真,如果没有到文件结尾返回假
    
    int ferror(FILE *stream);
    功能:如果在读文件的时候发生了错误,这个函数返回真
    参数:
        @stream:文件指针
    返回值:如果发生了错误返回真,如果没有发生错误返回假
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    4.1fread函数实例1(从文件读)

    #include 
    
    int main(int argc, const char *argv[])
    {
        FILE *fp;
        if (NULL == (fp = fopen("./1.txt", "r"))) {
            perror("fopen error");
            return -1;
        }
    
        // fread读取到错误或文件结尾返回,不会给读取的字符串数据填充'\0'。
        char buf[10] = {0};
        int ret;
        ret = fread(buf, 1, sizeof(buf)-1, fp);  //最好sizeof(buf)-1, 防止当读满buf时,buf后连续内存不为'\0'
    
        printf("buf = %s", buf);
        printf("ret = %d\n", ret); //9
        printf("feof = %d, ferror = %d\n", feof(fp), ferror(fp));  //用vim创建的1.txt, 当输入字符为 <= 7 + ('/n''/0') 
                                                                   //feof = 1, ferror = 0
                                                                   //用vscode创建的1.txt, 当输入里字符为 <= 8 + ('/0')
                                                                   //feof = 1, ferror = 0
    
        fclose(fp);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    4.2fread函数实例2(从文件读)

    #include 
    #include 
    
    int main(int argc, const char* argv[])
    {
        // 1.对输入参数的个数进行检查
        if (argc != 2) {
            printf("input error,try again\n");
            printf("usage: ./a.out filename\n");
            return -1;
        }
    
        // 2.以只读的方式打开文件
        FILE* fp;
        if ((fp = fopen(argv[1], "r")) == NULL) {
            perror("fopen error");
            return -1;
        }
    
        // 3.读取数据
        char buf[10] = {0};
        int ret;
        // 如果在读的时候没有到结尾,并且也没有错误,循环继续
        while (!(feof(fp) || ferror(fp))) {
            // 在读取之前将数组中的内容清零,如果没有这句,最后一行打印的结果不对
            // 因为fread读取到错误或文件结尾返回,不会给读取的字符串数据填充'\0'。
            memset(buf, 0, sizeof(buf));
            fread(buf, 1, sizeof(buf)-1, fp);
            printf("%s", buf);
        }
        
        fclose(fp);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    4.3fread函数实例3(读取整数、结构体)

    配合4.4fwrite函数实例4使用

    #include 
    
    typedef struct {
        char name[20];
        int age;
        char sex;
    }stu_t;
    
    int main(int argc, const char* argv[])
    {
        FILE* fp;
        if (NULL == (fp = fopen("./1.txt", "r"))) {
            perror("fopen error");
            return -1;
        }
    
        // 1.读取一个字符
        // char ch;
        // fread(&ch, 1, 1, fp);
        // printf("ch = %c\n", ch);
    
        // 2.读取数组
        // char buf[128] = {0};
        // fread(buf, 1, sizeof(buf)-1, fp);
        // printf("buf = %s\n", buf);
    
        // 3.读取整数
        // int num;
        // fread(&num, sizeof(num), 1, fp);
        // printf("num = %d\n", num);
    
        // 4.读取结构体
        stu_t stu;
        fread(&stu, sizeof(stu), 1, fp);
        printf("name=%s, age=%d, sex=%c\n",stu.name,stu.age,stu.sex);
        
        fclose(fp);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    4.4fwrite函数实例4(写入整数、结构体)

    配合4.3fread函数实例3使用

    #include 
    #include 
    
    typedef struct{
        char name[20];
        int age;
        char sex;
    }stu_t;
    
    int main(int argc, const char* argv[])
    {
        FILE* fp;
        if ((fp = fopen("./1.txt", "w")) == NULL) {
            perror("fopen error");
            return -1;
        }
        // 1.使用fwrite写一个字符
        // char ch='q';
        // fwrite(&ch, 1, 1, fp);
    
        // 2.使用fwrite写字符串
        // char buf[] = "hello everyone!";
        // fwrite(buf, 1, strlen(buf), fp);
    
        // 3.使用fwrite写整数
        // hello.txt是一个文本文件,文本文件中只能显示字符,并不能直接显示整数,
        // 但是数据确实是写进去了。写入的内容见下图
        // int num=12345;
        // fwrite(&num, sizeof(num), 1, fp);
    
        // 4.使用fwrite写结构体
        stu_t stu = {
            .name = "xiao ming",
            .age = 30,
            .sex = 'm'
        };
        fwrite(&stu, sizeof(stu), 1, fp);
    
        fclose(fp);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    4.5fwrite写整数到文件,文本内容解析

    在这里插入图片描述

    4.6fread、fwrite函数实例5(文件拷贝)

    #include 
    #include 
    
    int main(int argc, const char *argv[])
    {
        // 1.对输入参数的个数进行检查
        if (3 != argc) {
            fprintf(stderr, "Usage: %s  \n", argv[0]);
            return -1;
        }
    
        // 2.以只读的方式打开源文件,以只写方式打开目标文件
        FILE *sfp, *dfp;
        if (NULL == (sfp = fopen(argv[1], "r"))) {
            perror("fopen src_file error");
            return -1;
        }
        if (NULL == (dfp = fopen(argv[2], "w"))) {
            perror("fopen dest_file error");
            return -1;
        }
     
        // 3.循环拷贝
        // 如果在读的时候没有到结尾,并且也没有错误,循环继续
        char buf[10] = {0};
        int ret;
        while (!(feof(sfp) || ferror(sfp))) {
            ret = fread(buf, 1, sizeof(buf), sfp);
            fwrite(buf, 1, ret, dfp);
        }
    
        // 4.判断返回数据项个数也行
        // while (0 < (ret = fread(buf, 1, sizeof(buf), sfp))) {
        //     fwrite(buf, 1, ret, dfp);
        // }
    
        fclose(sfp);
        fclose(dfp);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    5.sprintf、snprintf、fprintf(格式化输出到内存、文件)

    sprintf、snprintf、fprintf函数API

    int sprintf(char *str, const char *format, ...);
    功能:将format控制格式的内容写到str对应的内存中(不会向终端显示)
    参数:
        @str:内存地址
        @format:和printf的参数完全相同
    返回值:成功返回格式化的字符的个数,失败返回负数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    int snprintf(char *str, size_t size, const char *format, ...);
    功能:将format控制格式的内容写到str对应的内存中(不会向终端显示)
    参数:
        @str:内存地址
        @size:最多格式化size个字符,其中还包括一个'\0'
    	@format:和printf的参数完全相同
    返回值:成功返回格式化的字符的个数,失败返回负数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    int fprintf(FILE *stream, const char *format, ...);
    功能:将控制格式格式化的字符串写入到文件中
    参数:
        @stream:文件指针
     	@format:控制格式
    返回值:成功返回格式化的字符的个数,失败返回负数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    5.1sprintf函数实例1

    #include 
    #include 
    
    typedef struct{
        char name[20];
        int age;
        char sex;
    }stu_t;
    int main(int argc, const char* argv[])
    {
        FILE* fp;
    
        if ((fp = fopen("./hello.txt", "w+")) == NULL) {
            perror("fopen error");
            return -1;
        }
        //1.使用fwrite写整数
        //hello.txt是一个文本文件,文本文件中只能显示字符,并不能直接显示整数,
        //但是数据确实是写进去了。
        int num=123456;
        char buf[10] = {0};
        sprintf(buf,"%d\n",num); 
        //使用sprintf函数的时候一定要注意越界问题,如果越界程序错误
        fwrite(buf,1,strlen(buf),fp);
    
        //2.使用fwrite写结构体
        stu_t stu = {
            .name = "zhangsan",
            .age = 30,
            .sex = 'm'
        };
        char buf1[50] = {0};
        sprintf(buf1,"%s,%d,%c\n",stu.name,stu.age,stu.sex);
        fwrite(buf1,1,strlen(buf1),fp);
    
        fclose(fp);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    5.2snprintf函数实例2

    #include 
    #include 
    
    typedef struct{
        char name[20];
        int age;
        char sex;
    }stu_t;
    int main(int argc, const char* argv[])
    {
        FILE* fp;
    
        if ((fp = fopen("./hello.txt", "w+")) == NULL) {
            perror("fopen error");
            return -1;
        }
        //1.使用fwrite写整数
        //hello.txt是一个文本文件,文本文件中只能显示字符,并不能直接显示整数,
        //但是数据确实是写进去了。
        int num=123456;
        char buf[10] = {0};
        snprintf(buf,sizeof(buf),"%d",num); 
        //使用sprintf函数的时候一定要注意越界问题,如果越界程序错误
        fwrite(buf,1,strlen(buf),fp);
    
        //2.使用fwrite写结构体
        stu_t stu = {
            .name = "zhangsan",
            .age = 30,
            .sex = 'm'
        };
        char buf1[50] = {0};
        snprintf(buf1,sizeof(buf1),"%s,%d,%c\n",stu.name,stu.age,stu.sex);
        fwrite(buf1,1,strlen(buf1),fp);
    
        fclose(fp);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    5.4fprintf函数实例(获取系统时间到终端显示)

    #include 
    #include 
    #include 
    
    int main(int argc, const char *argv[])
    {
    	time_t ts;
    	struct tm *tm;
    
    	while (1) {	
    		if (-1 == (ts = time(NULL))) {
    			perror("get time error");
    			return -1;
    		}
    		if (NULL == (tm = localtime(&ts))) {
    			perror("change time error");
    			return -1;
    		}	
    		fprintf(stdout, "%d-%02d-%02d %02d:%02d:%02d\r", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
    		fflush(stdout);
    		sleep(1);
    	}
    
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    6.time、localtime(获取系统时间,将系统时间转为本地时间)

    time、localtime函数API

    #include 
    time_t time(time_t *tloc);
    功能:获取自1970-01-01 00:00:00到当前的秒钟数
    参数:
        @tloc:NULL
    返回值:成功返回秒钟数,失败返回-1,并置位错误码
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    struct tm *localtime(const time_t *timep);
    功能:将time_t的秒钟转换为tm的结构体(结构体中包含年、月、日...)
    参数:
        @timep:秒钟变量的地址
    返回值:成功返回tm结构体指针,失败返回NULL,并置位错误码
           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) */   //+1
               int tm_year;   /* Year - 1900 */    //+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 */ //夏令时
           };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    8.fflush(刷新缓冲区)

    fflush函数API

    int fflush(FILE *fp);
    功能:将流缓冲区里的数据写入到实际的文件,Linux下只能刷新输出缓冲区
    参数:
    	@fp:文件指针
    返回值:成功返回0,失败返回EOF
    
    • 1
    • 2
    • 3
    • 4
    • 5

    9.fseek、rewind、ftell(定位文件指针)

    fseek、rewind、ftell函数API

    int fseek(FILE *stream, long offset, int whence);
    功能:设置光标的位置
    参数:
        @stream:文件指针
    	@offset:光标的偏移
            >0 向后偏移
            =0 不偏移
            <0 向前偏移
        @whence:从那个位置偏移
            SEEK_SET //从开头开始偏移
      SEEK_CUR //从当前位置偏移
            SEEK_END //从结尾的位置偏移
    返回值:成功返回0,失败返回-1置位错误码  
            
    eg:
     fseek(fp,0,SEEK_END); //将光标定位到文件的结尾
     fseek(fp,50,SEEK_SET);//将光标定位到第50个字节的位置
     fseek(fp,-5,SEEK_CUR);//将光标从当前位置向前偏移5个字节
    
     1. 文件a模式打开时,函数fseek无效
     2. rewind(fp)相当于fseek(fp, 0, SEEK_SET)
     3. 这3个函数只适用于2G以下的文件
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    void rewind(FILE *stream);
    功能:将光标恢复到文件的开头(rewind = fseek(stream, 0, SEEK_SET))
    参数:
        @stream:文件指针
    返回值:无
    
    • 1
    • 2
    • 3
    • 4
    • 5
    long ftell(FILE *stream);
    功能:返回光标到文件开头的字节数
    参数:
        @stream:文件指针
    返回值:成功返回字节数,失败返回-1置位错误码
    
    • 1
    • 2
    • 3
    • 4
    • 5

    9.1fseek函数实例1

    #include 
    
    #define PRINT_ERR(msg) \
        do {               \
            perror(msg);   \
            return -1;     \
        } while (0)
    
    int main(int argc, const char* argv[])
    {
        FILE* fp;
    
        if ((fp = fopen("./hello.txt", "r+")) == NULL)
            PRINT_ERR("fopen error");
    
        if (fseek(fp, 4, SEEK_SET))
            PRINT_ERR("fseek error");
    
        char ch;
        ch = fgetc(fp);
        printf("ch = %c\n",ch);
        fputc('Q',fp);
    
        fclose(fp);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    9.2ftell函数实例2(输出文件大小)

    #include 
    
    #define PRINT_ERR(msg) \
        do {               \
            perror(msg);   \
            return -1;     \
        } while (0)
    
    int main(int argc, const char* argv[])
    {
        FILE* fp;
    
        if(argc !=2){
            fprintf(stderr,"input error,try again\n");
            fprintf(stderr,"usage:./a.out filename\n");
            return -1;
        }
        if ((fp = fopen(argv[1], "r")) == NULL)
            PRINT_ERR("fopen error");
    
        if (fseek(fp, 0, SEEK_END))
            PRINT_ERR("fseek error");
    
        printf("pos = %ld\n",ftell(fp));
    
        fclose(fp);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    10.freopen(重定向输入输出流)

    在这里插入图片描述

    10.1freopen函数实例

    #include 
    
    int main(int argc, const char **argv)
    {
    	if (freopen("1.txt", "w", stdout) == NULL) {
    		perror("freopen");
    		return -1;
    	}
    	printf("stdout --> 1.txt\n");
    	
    	fclose(stdout);
    	
    	printf("end!\n");
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    三、缓冲区相关知识

    1.缓冲区的分类

    行缓存:和终端相关的缓冲区就是行缓存(stdin stdout)
    全缓存:和文件相关的缓冲区就是全缓存(fp)
    无缓存:没有缓冲区(stderr)

    2.缓冲区的大小

    行缓存:1024(1K)
    全缓存:4096(4K)
    无缓存:0

    #include 
    
    int main(int argc, const char *argv[])
    {
        // 1.stdin缓冲区大小 1024(使用了才分配)
        //FILE *stdin; --->FILE是一个结构体类型,stdin结构体指针,结构体指针访问内部成员->
        int num;
        scanf("%d", &num);
        printf("stdio buffer size = %ld\n", stdin->_IO_buf_end - stdin->_IO_buf_base);  //1024
    
        // 2.stdout缓冲区大小 1024
        printf("stdout buffer size = %ld\n", stdout->_IO_buf_end - stdout->_IO_buf_base);  //1024
    
        // 3.文件相关的缓冲区的大小 4096
        FILE *fp;
        if (NULL == (fp = fopen("./1.txt", "w"))) {
            perror("fopen error");
            return -1;
        }
        fputc(num, fp);
        printf("fp buffer size = %ld\n", fp->_IO_buf_end - fp->_IO_buf_base);  //4096
        
        fclose(fp);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    3.缓冲区的刷新时机

    3.1行缓冲的刷新时机(6种)

    1.行缓存遇到换行符的时候会刷新缓冲区
    2.当程序结束的时候会刷新行缓冲区
    3.当关闭文件的时候会刷新行缓冲区
    4.当输入和输出发生切换的时候也会刷新行缓冲区
    5.当缓冲区满的时候会刷新缓冲区
    6.自己使用fflush函数主动刷新缓冲区

    #include 
    
    int main(int argc, const char* argv[])
    {
        // 1.行缓存遇到换行符的时候会刷新缓冲区
        // printf("1111111111\n");
        // while(1);
    
        // 2.当程序结束的时候会刷新行缓冲区
        // printf("1111111111");
    
        // 3.当关闭文件的时候会刷新行缓冲区
        // printf("1111111111");
        // fclose(stdout);
        // while (1);
    
        // 4.当输入和输出发生切换的时候也会刷新行缓冲区
        // printf("1111111111");
        // fgetc(stdin);
        // while (1);
    
        // 5.当缓冲区满的时候会刷新缓冲区
        // for(int i = 0; i < 1025; i++){
        //     fputc('o',stdout);
        // }
        // while(1);
    
        // 6.自己使用fflush函数主动刷新缓冲区
        printf("1111111111");
        fflush(stdout); //刷新缓冲区的函数
        while(1);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    3.2全缓冲的刷新时机(5种)

    除了换行符不能刷新缓冲区, 其他和行缓冲区一样。

    1.当程序结束的时候会刷新行缓冲区
    2.当关闭文件的时候会刷新行缓冲区
    3.当输入和输出发生切换的时候也会刷新行缓冲区
    4.当缓冲区满的时候会刷新缓冲区
    5.自己使用fflush函数主动刷新缓冲区

    #include 
    
    int main(int argc, const char* argv[])
    {
        FILE* fp;
        if (NULL == (fp = fopen("./1.txt", "w"))) {
            printf("fopen error\n");
            return -1;
        }
        // 1.全缓存遇到换行符的时候不会刷新缓冲区
        // fputs("1111111111\n",fp);
        // while(1);
    
        // 2.当程序结束的时候会刷新全缓冲区
        // fputs("1111111111",fp);
    
        // 3.当关闭文件的时候会刷新全缓冲区
        // fputs("1111111111",fp);
        // fclose(fp);
        // while (1);
    
        // 4.当输入和输出发生切换的时候也会刷新全缓冲区
        //  fputs("1111111111",fp);
        //  fgetc(fp);
        //  while (1);
    
        // 5.当缓冲区满的时候会刷新全缓冲区
        //  for(int i = 0; i < 4097; i++){
        //      fputc('o',fp);
        //  }
        //  while(1);
    
        // 6.自己使用fflush函数主动刷新全缓冲区
        fputs("1111111111", fp);
        fflush(fp); // 刷新缓冲区的函数
        while (1);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
  • 相关阅读:
    NLP之基于Bi-LSTM和注意力机制的文本情感分类
    Mysql基本知识篇
    计算螺栓点云的高度【halcon三维计算】
    大数据中的一些词汇解释
    jdk版本与class文件格式major版本对应关系
    react-面试题
    [附源码]java毕业设计会议室会议管理系统
    数据标注行业中的“睁一只眼闭一只眼”
    前端学习 node 快速入门 系列 —— 事件循环
    概念解析 | 雷达协同认知成像:原理、研究现状与挑战
  • 原文地址:https://blog.csdn.net/weixin_50964793/article/details/128137712