• 0723------Linux基础----------文件 IO 之 read 和 write (readn 、writen、readline)


    1. readn 和 writen

      1.1 基础巩固: read 和 write 函数的返回值

        1.1.1 read 函数原型为:ssize_t  read(int fd, void* buf, size_t count); (这里的 void *在标准 C 中表示通用指针即任意类型的指针都可以对它赋值,ssize_t 是有符号整数)它的返回值如下:

          a)成功返回读取的字节数,这里可能等于 count 或者小于 count (当 count > 文件 size 的时候,返回实际读到的字节数);

          b)刚开始读就遇到EOF 则返回 0;

          c)读取失败返回 -1, 并设置相应的 errno。

     

        1.1.2 write 函数的原型为:ssize_t write(int fd, const void *buf, size_t count); 它的返回值如下:

          a)成功返回写入的字节数,这里同上;

          b)写入失败返回 -1,并设置相应的 errno;

          c)当返回值为0 时,表示什么也没有写进去,这种情况在socket编程中出现可能是因为连接已关闭,在写磁盘文件的时候一般不会出现。

     

      1.2 为什么要封装一个readn 函数writen 函数,现有的read 函数和 write 含有有什么缺陷? 

        这个是因为在调用read(或 write)函数的时候,读(写)一次的返回值可能不是我们想到读的字节数(即read函数中的 count 参数),这经常在读取管道,或者网络数去时出现。

     

      1.3 readn 函数 和 writen 函数

        1.3.1 readn保证在没有遇到EOF的情况下,一定可以读取n个字节它的返回值有三种:  

          a) >0,表示成功读取的字节数,如果小于n,说明中间遇到了EOF;

          b)==0 表示一开始读取就遇到EOF;

          c) -1 表示错误(这里的errno绝对不是EINTR)。

     

        1.3.2 writen函数保证一定写满n个字节,返回值:

          a)n 表示写入成功n个字节

            b)-1 写入失败(这里也没有EINTR错误)

     

      1.3.3 源码如下,举例说明,当在while循环中调用readn(fd, buf, 20)去读取一个大小为55字节的文件时,会调用 4 次该readn函数

        a)第一次 返回 20;

        b)第二次 返回 20;

        c)第三次 返回 15(这里在readn函数内部会调用2次read函数,第一次只能读15字节,因为到EOF了,此时还差5字节,再读的的时候read直接返回0);

        d)第四次 返回 0 , 表示文件读完,跳出 while ,不会在进入 while 内部。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <errno.h>
    #define ERR_EXIT(m) 
        do { 
            perror(m);
            exit(EXIT_FAILURE);
        }while(0)
    
    /*
     * readn 和 wirten 函数
     */
    
    ssize_t readn(int fd, void *buf, int n);
    ssize_t writen(int fd, void *buf, int n);
    
    
    /*
     * 这里 readn 函数成功返回读取的字节数
     * 如果字节数小于n 一定是遇到了 EOF
     * 出错返回 -1 这里的错误一定不包含 EINTR
     * 一开始就遇到EOF 返回0
     */
    
    ssize_t readn(int fd, void *buf, int n){
        size_t nleft = n; //还需要读取的字节数
        char *bufptr = buf; //指向read函数当前存放数据的位置
        ssize_t  nread;
    
        while(nleft > 0){
            if((nread = read(fd, bufptr, n)) < 0){
                if(errno == EINTR){ //遇到中断
                    continue;
                }
                else            // 其他错误
                    return -1;
            }
            else if(nread == 0){ // 遇到EOF
                break;
            }
    
            nleft -= nread;
            bufptr += nread;
        }
    
        return (n - nleft);
    }
    /*
     * 这里的 writen 必须写满 n 个字节
     * 少于 n 就属于错误
     * 返回 n  或者 -1
     */
    
    ssize_t writen(int fd, void *buf, int n){
        size_t nleft = n;
        char *bufptr = buf;
        ssize_t nwrite;
    
        while(nleft > 0){
            if((nwrite = write(fd, bufptr, n)) <= 0){
                if(errno == EINTR)
                    nwrite = 0;
                else
                    return -1;
            }
    
            nleft -= nwrite;
            bufptr += nwrite;
        }
    
        return n; //  注意这里必须是 n 因为这里保证了 n 字节都被写入
    }
    
    int main(int argc, const char *argv[])
    {
        char buf[20] = {0};
        int fd = open("test.txt", O_RDONLY);
        if(fd == -1){
            ERR_EXIT("open");
        }
    
        int ret;
        while(printf("-----call readn----
    "), (ret = readn(fd, buf, 20)) > 0){
            writen(STDOUT_FILENO, buf, ret);
        }
    
        close(fd);
        return 0;
    }
    

      

    2. readline 函数 

      2.1 readline函数:ssize_t readline(int fd, void *usrbuf, size_t maxlen),它的返回值:

        a)错误返回-1,不包括EINTR;

        b)取过程中碰到 ;

        c)没有碰到 ,而是读满maxlen-1个字节。

     

      2.2 源码。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <errno.h>
    #define ERR_EXIT(m) 
        do { 
            perror(m);
            exit(EXIT_FAILURE);
        }while(0)
    
    /*
     * 用 read 实现 readline 函数 每次读取一个字节 判断是不是 
    
     *
     */
    
    ssize_t readline(int fd, void *usrbuf, size_t maxlen){
        char *bufptr = usrbuf;
        char c;
        size_t nleft = maxlen - 1; // 为 预留一个位置
        int nread;
    
        while(nleft > 0){
            if((nread = read(fd, &c, 1)) <  0){
                if(errno == EINTR){
                    continue;
                }
                else
                    return -1;
            }
            else if(nread == 0){ //EOF
                break;
            }
    
            *bufptr++ = c;
            nleft --;
    
            if(c == '
    '){ //遇到 
     就结束
                break;
            }
        }
    
        *bufptr = '';
        return (maxlen - 1 - nleft);
    }
    
    
    int main(int argc, const char *argv[])
    {
        int fd = open("test.txt", O_RDONLY);
        if(fd == -1){
            ERR_EXIT("open");
        }
        char buf[1024] = {0};
        int ret;
        while((ret = readline(fd, buf, 1000)) > 0){
            printf("ret = %d
    buf = %s",ret, buf);
        }
        return 0;
    }
    

      

      

      2.3 readn、writen、readline属于同一个系列,称为网络编程三大函数。

     

    3. RIO 一个用缓冲区实现的IO系统

      3.1 RIO中rio_read函数的编写思想:

        a)用预读取方案,提前把数据读入Buffer;

        b)每当用户取数据的时候,从Buffer里面读取,而不是使用系统调用read函数,这样减少了多次使用系统调用的开销。

     

       3.2 rio_read函数的编写原则:必须与系统的 read 函数保持语义一致,这意味着rio_read的返回值有三种情况:

        a) -1代表出错,这里不包含EINTR;

        b) 0代表EOF,读取结束;

        c) >0表示读取的字节数。

     

      3.3 源码实现见。

     

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <errno.h>
    #define ERR_EXIT(m) 
        do { 
            perror(m);
            exit(EXIT_FAILURE);
        }while(0)
    #define BUF_LEN 8192
    
    /*
     * RIO 一个带有缓冲区的IO系统
     * 每次读取都从缓冲区中读取 而不是系统调用
     *
     */
    
    typedef struct{
        int fd_;     //要读取的fd
        size_t nleft_;  // 缓冲区剩余可用的数据字节数
        char *bufptr_;  //指向剩余可用数据的起止地址
        char buffer_[BUF_LEN];
    }Rio_t;
    
    void rio_init(Rio_t *rp, int fd);
    ssize_t rio_read(Rio_t *rp, void *usrbuf, size_t n);
    ssize_t rio_readn(Rio_t *rp, void *usrbuf, size_t n);
    ssize_t rio_readline(Rio_t *rp, void *usrbuf, size_t maxline);
    ssize_t rio_writen(int fd, void *usrbuf, size_t n);
    
    
    
    void rio_init(Rio_t *rp, int fd){ //初始化一个RIO
        rp->fd_ = fd;
        rp->nleft_ = 0;
        rp->bufptr_ = rp->buffer_;
    }
    
    ssize_t rio_read(Rio_t *rp, void *usrbuf, size_t n){ //将数据预读取入 buffer中
    
        int nread;
        while(rp->nleft_ == 0){  // 当前缓冲区中没有可用的数据
            if((nread = read(rp->fd_, rp->buffer_, sizeof(rp->buffer_))) < 0){
                if(errno == EINTR){
                    continue;
                }
                else
                    return -1;
            }
            else if(nread == 0){ // EOF
                return 0;
            }
            rp->nleft_ = nread;
            rp->bufptr_ = rp->buffer_;//重置缓冲区指针
        }
        // 此时缓冲区中已经有数据
        int cnt = n;
    
        if(rp->nleft_ < n) //缓冲区可提供的字节数小于用户要求的字节数
            cnt = rp->nleft_;
    
        memcpy(usrbuf, rp->bufptr_, cnt);
    
        rp->nleft_ -= cnt; //读取后 可用字节数减少
        rp->bufptr_ += cnt;
    
        return  cnt;
    }
    
    ssize_t rio_readn(Rio_t *rp, void *usrbuf, size_t n){
        char *bufptr = usrbuf;
        size_t  nleft = n;
        size_t  nread;
    
        while(nleft > 0){
            if((nread = rio_read(rp, bufptr, nleft)) == -1){ //neft
                return -1;
            }
            else if(nread == 0){
                break;
            }
    
            nleft -= nread;
            bufptr += nread;
        }
        return (n - nleft);
    
    }
    
    ssize_t rio_readline(Rio_t *rp, void *usrbuf, size_t maxline){
        size_t nleft = maxline - 1;
        char *bufptr = usrbuf;
        char c;
        int nread;
    
        while(nleft > 0){
            if((nread = rio_read(rp, &c, 1)) == -1)
                return -1;
            else if(nread == 0){
                break;
            }
            *bufptr++ = c;
            nleft --;
    
            if(c == '
    ')
                break;
        }
        *bufptr = '';
        return (maxline - 1 - nleft);
    }
    
    ssize_t rio_writen(int fd, void *usrbuf, size_t n){
        char *bufptr = usrbuf;
        size_t nleft = n;
        size_t nwrite;
    
        while(nleft > 0){
            if((nwrite = write(fd, bufptr, n)) < 0){
                if(errno == EINTR)
                    continue;
                else
                    return -1;
             }
    
            nleft -= nwrite;
            bufptr += nwrite;
        }
        return n;
    }
    
    int main(int argc, const char *argv[])
    {
        Rio_t  r;
        int fd = open("test.txt", O_RDONLY);
        if(fd == -1){
            ERR_EXIT("open");
        }
    
        rio_init(&r, fd);
        int ret;
        char buf[50] = {0};
        while((ret = rio_readline(&r, buf, 50)) >0){
            rio_writen(STDOUT_FILENO, buf, ret);
        }
        return 0;
    }
    

      

     

     

     

     

     

     

     




  • 相关阅读:
    第一周作业
    模拟赛3 题解
    模拟赛2 题解
    [HNOI2008]GT考试 题解
    NOI Online 提高组 题解
    模拟赛1 题解
    知识点拾遗
    [NOIp2012]疫情控制 题解
    [CEOI2002]Bugs Integrated, Inc. 题解
    [NOIp2017]宝藏 题解
  • 原文地址:https://www.cnblogs.com/monicalee/p/3866410.html
Copyright © 2020-2023  润新知