day22——IO进程

一、IO基础

1> IO:(inout output) 所谓IO,就是程序与外部设备进行信息交换的过程

2> IO的分类:标准IO和文件IO

3> 标准IO:调用封装好的相关库函数,来实现数据的输入输出

4> 文件IO:调用系统(内核)提供的相关函数,来实现数据的输入输出

5> 标准IO和文件IO的区别:

1、标准IO属于库函数,文件IO属于系统调用

2、标准IO操作的是文件指针,文件IO操作的是文件描述符

3、标准IO有缓冲器,文件IO没有缓冲区

4、标准IO中封装了文件IO

6> 阻塞IO和非阻塞IO

7> 目前所接触过的IO函数:

printf、scanf、putchar、getchar、puts、gets

8> 常用的IO接口函数

标准IO:fprint、fscanf、fputc、fgetc、fputs、fgets、fread、 fwrite、fopen、fclose、fseek、ftell、rewind。。。

文件IO:open、close、read、write、seek。。。

9> IO操作流程

二、标准IO

2.1 标准IO提供的内容

可以通过指令:man 7 stdio.h 可以查看该头文件提供的内容

1、标准的缓冲输入输出
2、提供的数据类型
       FILE          文件结构体类型.



       off_t         偏移量类型

       size_t        大小的类型,一般是一个long int类型
3、提供的偏移量的宏值
        SEEK_SET:文件起始位置
        SEEK_END:文件结束位置
        SEEK_CUR:文件当前位置
4、提供文件结束标志:EOF (end of file)
5、默认提供的文件指针:
        stdin:标准输入指针
        stdout:标准输出指针
        stderr:标准出错指针
6、标准IO会提供三种缓冲区:
        全缓存
        行缓存
        不缓存                                              

2.2 FILE结构体

1> 如何追该结构体:可以使用 vi -t FILE

2> 结构体解析

245 struct _IO_FILE {


257   char* _IO_buf_base;   /* 缓冲区开始地址 */
258   char* _IO_buf_end;    /* 缓冲区结束地址 */


267 
268   int _fileno;         //用于系统调用的文件描述符

286 };    

3> 当一个程序启动后,系统会自动打开三个文件指针:

stdin:标准输入文件指针 ---> scanf、getchar、gets

stdout:标准输出文件指针 ----> printf、putchar、puts

stderr:标准出错文件指针 ---> perror

以上三个文件指针,都是针对于终端文件操作而言的

2.3 fopen 打开文件

       #include <stdio.h>

       FILE *fopen(const char *pathname, const char *mode);
    功能:以指定的方式打开一个给定的文件,并返回该文件的文件地址
    参数1:要打开的文件文件路径,可以是相对路径,也可以是绝对路径
    参数2:文件打开的方式,需要以以下字符开头的字符串
       r     以只读的形式打开文件,文件光标定位在开头.如果文件不存在,则报错

       r+     以读写的形式打开文件,文件光标定位在开头.如果文件不存在,则报错

       w      以只写的形式打开文件,如果文件存在,则清空文件内容,如果文件不存在,则创建该文件,文件光标定位在开头.

       w+     以读写的形式打开文件,如果文件存在,则清空文件内容,如果文件不存在,则创建该文件,文件光标定位在开头.

       a      以追加的形式打开文件,如果文件不存在则创建文件,文件光标定位在结尾

       a+     以读或者追加的形式打开文件,如果文件不存在,则创建文件,如果第一次是读数据,则光标定位在开头,否则定位在结尾
    返回值:成功调用返回打开的文件地址,失败返回NULL,并置位错误码

2.4 fclose:关闭文件

       #include <stdio.h>

       int fclose(FILE *stream);
    功能:关闭给定的文件指针
    参数1:要关闭的文件指针
    返回值:成功返回0,失败返回EOF,并置位错误码
#include<myhead.h>

int main(int argc, const char *argv[])
{
    //定义文件类型的结构体以便于接受打开的文件地址
    FILE * fp = NULL;

    //打开一个文件
    //fp = fopen("./text.txt", "r");           //以只读的形式打开一个当前路径下的文件
    fp = fopen("./text.txt", "w");           //以只写的形式打开一个当前路径下的文件
    if(NULL == fp)
    {
        printf("fopen error\n");
        return -1;
    }

    printf("fopen success");   
    

    //关闭文件指针
    fclose(fp);


    return 0;
}

2.5 fgetc\fputc:单字符的输入输出

       #include <stdio.h>

       int fputc(int c, FILE *stream);
    功能:将给定的字符,写入到文件指针stream指向的文件中去
    参数1:要写入的字符
    参数2:打开的文件指针
    返回值:成功返回写入字符的ascii值,失败返回EOF,并置位错误码
    
           #include <stdio.h>

       int fgetc(FILE *stream);
       功能:从指定文件中,读取一个字符
       参数:打开的文件指针
       返回值:从文件中读取的第一个字符的ascii值,失败返回EOF并置位错误码       
    
#include<myhead.h>

int main(int argc, const char *argv[])
{
    //定义文件类型的结构体以便于接受打开的文件地址
    FILE * fp = NULL;

    //打开一个文件
    //fp = fopen("./text.txt", "r");           //以只读的形式打开一个当前路径下的文件
    fp = fopen("./text.txt", "w+");           //以只写的形式打开一个当前路径下的文件
    if(NULL == fp)
    {
        printf("fopen error\n");
        return -1;
    }

    printf("fopen success\n");

    //向文件中写入字符
    fputc('H', fp);
    fputc('e', fp);
    fputc('l', fp);
    fputc('l', fp);
    fputc('o', fp);
    fputc('\n', fp);
    //文件中存储的结果为 Hello  
    //说明每次写入数据时,光标会自动后移
    

    //关闭文件指针
    fclose(fp);

    //再次以只读的形式打开文件
    fp = fopen("./text.txt", "r");           //以只写的形式打开一个当前路径下的文件
    if(NULL == fp)
    {
        printf("fopen error\n");
        return -1;
    }


    //定义变量用于读取数据
    char buf = 0;
    while(1)
    {
        buf = fgetc(fp);       //从fp中读取数据
        if(EOF == buf)          //表示文件读取结束
        {
            break;
        }

        printf("%c\t", buf);   //将读取的数据显示到终端
    }

    //关闭文件指针
    fclose(fp);


  
    return 0;
}

练习:

1、使用fgetc完成,打开一个文件,统计该文件中的行数

#include<myhead.h>

int main(int argc, const char *argv[])
{
    //判断传入的参数是否为2个
    if(argc != 2)
    {
        printf("input file error!!!\n");
        printf("usage:./a.out fileName\n");
        return -1;
    }


    //表示传过来的有一个文件
    //定义文件指针
    FILE *fp = NULL;
    if((fp = fopen(argv[1], "r")) == NULL)
    {
        printf("fopen error\n");
        return -1;
    }

    //说明文件已经打开
    char buf = 0;        //接受字符
    int count = 0;       //统计行数
    while(1)
    {
        //从文件中读取一个字符
        buf = fgetc(fp);
        if(buf == EOF)
        {
            break;
        }

        //判断是否读取到回车
        if(buf == '\n')
        {
            count ++;
        }
    }

    //输出行号
    printf("该文件一共有%d行\n", count);

    //关闭文件
    fclose(fp);
    return 0;
}

2、使用fgetc和fputc完成,cp指令的功能,实现两个文件的拷贝,将src文件中的内容,拷贝到dest文件中

#include<myhead.h>

int main(int argc, const char *argv[])
{
    //判断传入的是否为三个文件
    if(argc != 3)
    {
        printf("input file error\n");
        printf("usage:./a.out srcfile destfile\n");
        return -1;
    }

    //以只读的形式打开源文件
    FILE *sfp = NULL;
    if((sfp = fopen(argv[1], "r")) == NULL)
    {
        printf("open src file error\n");
        return -1;
    }

    //以只写的形式打开目标文件
    FILE *dfp = NULL;
    if((dfp = fopen(argv[2], "w")) == NULL)
    {
        printf("open destfile error\n");
        return -1;
    }

    //定义搬运工
    char buf = 0;
    //循环从源文件中读取数据放入到目标文件中
    while(1)
    {
        buf = fgetc(sfp);        //从源文件中读取
        if(buf == EOF)
        {
            break;
        }

        //将读取的数据写入目标文件中
        fputc(buf, dfp);
    }

    //关闭文件
    fclose(sfp);
    fclose(dfp);

    printf("拷贝成功\n");


    return 0;
}

2.6 有关错误码问题

1> 错误码是调用内核提供的函数时,如果调用出错,那么内核空间会向用户空间反馈一个错误信息

2> 错误信息千奇百怪,并且是字符串内容,为了方便起见,给这些错误信息,进行编号

3> 如果,内核空间函数调用出问题时,只需要反馈错误编号即可,这个错误编号就叫做错误码

4> 常见错误码:可以通过指令 vi -t EIO 查看

  5 #define EPERM        1  /* Operation not permitted */
  6 #define ENOENT       2  /* No such file or directory */
  7 #define ESRCH        3  /* No such process */
  8 #define EINTR        4  /* Interrupted system call */
  9 #define EIO      5  /* I/O error */
 10 #define ENXIO        6  /* No such device or address */
 11 #define E2BIG        7  /* Argument list too long */
 12 #define ENOEXEC      8  /* Exec format error */
 13 #define EBADF        9  /* Bad file number */
 14 #define ECHILD      10  /* No child processes */
 15 #define EAGAIN      11  /* Try again */
 16 #define ENOMEM      12  /* Out of memory */
 17 #define EACCES      13  /* Permission denied */
 18 #define EFAULT      14  /* Bad address */
 19 #define ENOTBLK     15  /* Block device required */
 20 #define EBUSY       16  /* Device or resource busy */
 21 #define EEXIST      17  /* File exists */
 22 #define EXDEV       18  /* Cross-device link */
 23 #define ENODEV      19  /* No such device */                                                            
 24 #define ENOTDIR     20  /* Not a directory */
 25 #define EISDIR      21  /* Is a directory */
 26 #define EINVAL      22  /* Invalid argument */
 27 #define ENFILE      23  /* File table overflow */
 28 #define EMFILE      24  /* Too many open files */
 29 #define ENOTTY      25  /* Not a typewriter */
 30 #define ETXTBSY     26  /* Text file busy */
 31 #define EFBIG       27  /* File too large */
 32 #define ENOSPC      28  /* No space left on device */
 33 #define ESPIPE      29  /* Illegal seek */
 34 #define EROFS       30  /* Read-only file system */
 35 #define EMLINK      31  /* Too many links */
 36 #define EPIPE       32  /* Broken pipe */
 37 #define EDOM        33  /* Math argument out of domain of func */
 38 #define ERANGE      34  /* Math result not representable */

5> 有关错误码的相关函数

1、strerror函数
              #include <string.h>

       char *strerror(int errnum);
    功能:将给定的错误码,转变成错误信息
    参数1:错误码
    返回值:错误码对应的错误信息的字符串
    
    错误码如何获得?
    答:需要加一个头文件:#include<errno.h>
    #include <errno.h>

    const char * const sys_errlist[];
    int sys_nerr;
    int  errno;        /*  Not  really  declared  this  way;  see
 errno(3) */

2、perror函数
       #include <stdio.h>

       void perror(const char *s);
    功能:向标准出错缓冲区中,写入最新的错误码对应的信息
    参数:提示字符串,会自动提供一个冒号,并且输出结束后,会自动加一个换行
    返回值:无
    
#include<myhead.h>

int main(int argc, const char *argv[])
{
    //定义文件指针
    FILE *fp = NULL;
    if((fp = fopen("./file.txt", "r")) == NULL)
    {
        //strerror:将错误码转变成错误信息
        //printf("errno:%s\n", strerror(errno));
        perror("fopen error");

        return -1;
    }

    printf("open success\n");

    return 0;
}

2.7 fputs\fgets:字符串输入输出

       #include <stdio.h>


       int fputs(const char *s, FILE *stream);
    功能:将给定的字符串,写入到文件中
    参数1:要写入的字符串起始地址
    参数2:打开的文件指针
    返回值:成功返回写入的字符个数(字符串长度),失败返回EOF
    
      #include <stdio.h>


       char *fgets(char *s, int size, FILE *stream);
       功能:从stream所指向的文件中,最多读取size-1的字符到s中,在读取过程中,如果遇到回车或者文件结束,会结束本次读取,并且会把回车也放入容器中。在后面自动加上'\0'
       参数1:存放数据的容器,一般是一个字符数组
       参数2:读取的大小
       参数3:文件指针
       返回值:成功返回容器的起始地址,失败返回NULL

fputs案例

#include<myhead.h>

int main(int argc, const char *argv[])
{
    //定义文件指针
    FILE *fp = NULL;
    if((fp = fopen("./file.txt", "w")) == NULL)
    {
        //strerror:将错误码转变成错误信息
        //printf("errno:%s\n", strerror(errno));
        perror("fopen error");

        return -1;
    }

    printf("open success\n");

    //向文件中写入字符串
    fputs("Hello world\n", fp);
    fputs("I love China\n", fp);
    fputs("Hua qing yuan jian\n", fp);
    fputs("good good study day day up\n", fp);


    //关闭文件
    fclose(fp);



    return 0;
}

fgets和fputs案例

#include<myhead.h>

int main(int argc, const char *argv[])
{
    //定义文件指针
    FILE *fp = NULL;
    if((fp = fopen("./file.txt", "w")) == NULL)
    {
        //strerror:将错误码转变成错误信息
        //printf("errno:%s\n", strerror(errno));
        perror("fopen error");

        return -1;
    }

    printf("open success\n");

    //向文件中写入字符串
    fputs("Hello world\n", fp);
    fputs("I love China\n", fp);
    fputs("Hua qing yuan jian\n", fp);
    fputs("good good study day day up\n", fp);

    //关闭文件
    fclose(fp);

    //以只读的形式重新打开文件
    if((fp = fopen("./file.txt", "r")) == NULL)
    {
        perror("fopen error");
        return -1;
    }

    //循环读取数据
    char buf[128] = "";
    while(1)
    {
        //清空容器
        bzero(buf,  sizeof(buf));
        if( fgets(buf, sizeof(buf), fp)  == NULL)
        {
            break;    //说明文件读取结束
        }

        //输出读取的数据
        printf("%s", buf);   
    }

    printf("\n");

    //关闭文件
    fclose(fp);



    return 0;
}

2.8 关于缓冲区

1> 标准IO提供了三种缓冲区:行缓存、全缓存、不缓存

2> 行缓存:有关标准输入、标准输出指针对应的缓冲区,其大小位1024字节

3> 全缓存:有关普通文件指针对应的缓冲区,其大小位4096字节

4> 不缓存:有关标准出错文件指针对应的缓冲区,其大小位 0

#include<myhead.h>

int main(int argc, const char *argv[])
{
    printf("行缓存的大小为:%ld\n", stdout->_IO_buf_end - stdout->_IO_buf_base);//0
    printf("行缓存的大小为:%ld\n", stdout->_IO_buf_end - stdout->_IO_buf_base);//1024
    getchar();        //使用一次标准输入
    printf("行缓存的大小为:%ld\n", stdin->_IO_buf_end - stdin->_IO_buf_base);//1024
    perror("usage");      //使用一次标准出错
    printf("不缓存的大小为:%ld\n", stderr->_IO_buf_end - stderr->_IO_buf_base);//0

    //验证全缓冲
    FILE *fp = NULL;
    if((fp = fopen("./tt.c", "w+")) == NULL)
    {
        perror("fopen error");
        return -1;
    }

    fgetc(fp);        //使用一次全缓冲
    printf("全缓存的大小为:%ld\n", fp->_IO_buf_end - fp->_IO_buf_base);//0
    


    //关闭
    fclose(fp);


    return 0;
}

5> 行缓存的刷新时机

1、换行会刷新行缓存

2、程序结束后,会自动刷新行缓存

3、当文件指针关闭后,会刷新行缓存

4、当使用fflush函数刷新文件指针时,会刷新行缓存

5、当输入输出切换时,会刷新行缓存

6、当缓存区满了后,再放数据时,会刷新行缓存

#include<myhead.h>

int main(int argc, const char *argv[])
{
    /*1、如果行缓存刷新时机未到,则不会刷新
    printf("hello world");            //不会输出
    while(1);
    */

    /*2、遇到换行,会刷新行缓冲
    printf("hello world\n");            //不会输出
    while(1);
    */

    /*3、当程序结束后,会刷新行缓冲
    printf("hello world");
    */

    /*4、当文件指针关闭后,会刷新行缓冲
    printf("hello world");
    fclose(stdout);            //关闭标准输出文件指针
    while(1);
    */

    /*5、手动调用fflush刷新缓冲器时,会刷新行缓冲
    printf("hello world");
    fflush(stdout);
    while(1);
    */


    /*6、当输入输出发生切换时,也会刷新行缓冲
    int num = 0;
    printf("请输入num的值:");
    scanf("%d", &num);
    */

    //7、当行缓冲区满了后,再往缓冲器区中放数据时,会刷新行缓冲
    
    for(int i=0; i<1999; i++)
    {
        printf("A");
    }
    while(1);


    return 0;
}

6> 全缓存的刷新时机

1、换行不会刷新全缓冲

2、程序结束后,会自动刷新全缓存

3、当文件指针关闭后,会刷新全缓存

4、当使用fflush函数刷新文件指针时,会刷新全缓存

5、当输入输出切换时,会刷新全缓存

6、当缓存区满了后,再放数据时,会刷新全缓存

#include<myhead.h>

int main(int argc, const char *argv[])
{
    //定义文件指针
    FILE *fp = NULL;
    if((fp = fopen("./tt.c", "w+")) == NULL)
    {
        perror("fopen error");
        return -1;
    }

    /*1、换行不会刷新全缓冲
    fputs("hello a\n", fp);
    while(1);
    */

    /*2、程序结束后,会刷新全缓冲
    fputs("hello a\n", fp);
    */

    /*3、关闭文件指针时,会刷新全缓冲
    fputs("hello a\n", fp);
    fclose(fp);
    while(1);
    */
    
    /*4、使用fflush刷新文件指针时,会刷新全缓冲
    fputs("hello a\n", fp);
    fflush(fp);
    while(1);
    */

    /*5、输入输出发生切换时,会刷新全缓冲
    fputs("hello a\n", fp);
    fgetc(fp);
    while(1);
    */

    //6、当缓冲区满了后,会刷新全缓冲
    for(int i=0; i<4097; i++)
    {
        fputc('A', fp);
    }
    while(1);



    return 0;
}

2.9 标准IO函数也可以使用stdin、stdout、stderr

#include<myhead.h>

int main(int argc, const char *argv[])
{
    char buf[20] = "";

    printf("请输入一个字符串:");
    fgets(buf, sizeof(buf), stdin);    //从终端读取数据到buf中
    buf[strlen(buf)-1] = '\0';   //将回车换成 '\0'
    printf("buf = %s", buf);


    fputs("我是一个错误", stderr);   //向标准出错中写入数据

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值