• 你好!欢迎你的到来
  • 关于我们
  • 首页 博客 学习笔记 技术导航 工具
  • 博文分类
    • PHP(43)
    • MySQL(11)
    • Linux(28)
    • html(3)
    • JQuery(4)
    • JavaScript(9)
    • svn(2)
    • CSS(2)
    • seajs(1)
    • go(44)
    • redis(1)
    • nginx(8)
    • mongo(0)
    • es(0)
    • 算法(0)
    • 其他(26)
    • 生活(1)
    专栏
    • Jquery基础教程
      • 文章:(15)篇
      • 阅读:17005
    • shell命令
      • 文章:(42)篇
      • 阅读:58050
    • Git教程
      • 文章:(36)篇
      • 阅读:114512
    • leetCode刷题
      • 文章:(37)篇
      • 阅读:14417
    • 摘要视图
    • 目录视图
    如何从io.Reader 中读数据
    2019-03-25 13:06 阅读(796) 评论(0)

    一、概述

    开发过程中,我们经常从io.Reader中读取数据。

    type Reader interface {
       Read(p []byte) (n int, err error)
    }

    1、一次最多读取len(p)长度的数据

    2、当读取遭遇到error或EOF, 会返回已读取的数据的字节数和error或EOF

    3、Read方法,不会修改len(p)的大小

    4、使用io.EOF 代表结束了

    Talk is cheap. Show me the code ,下面是一个从read读取的案例

    package main
    
    import (
       "fmt"
       "io"
       "net"
    )
    
    func main() {
       // 建立tcp连接
       conn, err := net.Dial("tcp", "www.findme.wang:80")
       if err != nil {
          fmt.Println("dial error:", err)
          return
       }
    
       defer conn.Close() // 关闭连接
    
       // 构建http协议内容,发起http请求
       httpReq := `GET / HTTP/1.0
    Host: www.findme.wang
    User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36
    Content-Type:application/x-www-form-urlencoded
    Content-Length:0
    
    `
       _, err = fmt.Fprintf(conn, httpReq)
       if err != nil {
          fmt.Println("http request error:", err)
          return
       }
    
       // read from conn
       rsData := make([]byte, 0)
    
       for {
          // 每次最多读取512 个字节
          var tmp = make([]byte, 512)
          n, err := conn.Read(tmp)
    
          if n >= 0 {
             rsData = append(rsData, tmp[:n]...)
          }
    
          if err == io.EOF {
             fmt.Println("数据读取完毕")
             break
          } else if err != nil {
             fmt.Println("读取数据报错:", err)
             break
          }
       }
    
       fmt.Println("读取的数据长度:", len(rsData))
    }

    在案例中,我们利用for循环反复的读,有没有简洁的方式呢?

    二、利用io.copy读取

    io.copy定义如下:

    func Copy(dst Writer, src Reader) (written int64, err error) {
       return copyBuffer(dst, src, nil)
    }

    将reader中内容读取到dst中的数据,读取到dst中,所以我们需要一个writer 就行,来吧,封装一个如下:

    package main
    
    import (
       "fmt"
       "io"
       "net"
    )
    
    type MyWriter struct {
       data []byte
    }
    
    func (m *MyWriter) Write(p []byte) (n int, err error) {
       if m.data == nil {
          m.data = make([]byte, 0)
       }
    
       if p != nil && len(p) != 0 {
          m.data = append(m.data, p...)
       }
    
       return len(p), nil
    }
    
    func main() {
       // 建立tcp连接
       conn, err := net.Dial("tcp", "www.findme.wang:80")
       if err != nil {
          fmt.Println("dial error:", err)
          return
       }
    
       defer conn.Close() // 关闭连接
    
       // 构建http协议内容,发起http请求
       httpReq := `GET / HTTP/1.0
    Host: www.findme.wang
    User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36
    Content-Type:application/x-www-form-urlencoded
    Content-Length:0
    
    `
    
    
       _, err = fmt.Fprintf(conn, httpReq)
       if err != nil {
          fmt.Println("http request error:", err)
          return
       }
    
       w := new(MyWriter)
       n, err := io.Copy(w, conn) // 将 conn中的数据读取到 writer中
       if err != nil {
         fmt.Println("读取err ", err)
       }
       //fmt.Println(string(w.data))// 打印数据
       fmt.Println("读取的数据长度:", n)
    }

    从io读取数据虽然是简单了,但是需要封装一个writer。那么,go里面是否有类似的writer呢?能够让我们很容易获取数据的writer呢?

    于是,我们找到了strings.buffer ,如下:

    // A Builder is used to efficiently build a string using Write methods.
    // It minimizes memory copying. The zero value is ready to use.
    // Do not copy a non-zero Builder.
    type Builder struct {
       addr *Builder // of receiver, to detect copies by value
       buf  []byte
    }

    有了strings.buffer,代码又可精简一波。

    package main
    
    import (
       "fmt"
       "io"
       "net"
       "strings"
    )
    
    
    func main() {
       // 建立tcp连接
       conn, err := net.Dial("tcp", "www.findme.wang:80")
       if err != nil {
          fmt.Println("dial error:", err)
          return
       }
    
       defer conn.Close() // 关闭连接
    
       // 构建http协议内容,发起http请求
       httpReq := `GET / HTTP/1.0
    Host: www.findme.wang
    User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36
    Content-Type:application/x-www-form-urlencoded
    Content-Length:0
    
    `
       _, err = fmt.Fprintf(conn, httpReq)
       if err != nil {
          fmt.Println("http request error:", err)
          return
       }
    
       var sb strings.Builder
       n, err := io.Copy(&sb, conn) // 将 conn中的数据读取到 writer中
       if err != nil {
         fmt.Println("读取err ", err)
       }
       fmt.Println(sb.String()) // print res
       fmt.Println("读取的数据长度:", n)
    }

    除了,使用strings.buffer,我们还可以使用bytes.Buffer。

    三、使用ioutil.ReadAll

    ReadAll(r io.Reader) ([]byte, error) 是一次性从输入流(reader)中读取全量数据,直到发送错误或EOF。若读取失败,返回已读数据和err;若读取成功,则返回全量数据和nil。即改方法,不会返回EOF,

    案例如下:

    package main
    
    import (
       "fmt"
       "io/ioutil"
       "net"
    )
    
    
    func main() {
       // 建立tcp连接
       conn, err := net.Dial("tcp", "www.findme.wang:80")
       if err != nil {
          fmt.Println("dial error:", err)
          return
       }
    
       defer conn.Close() // 关闭连接
    
       // 构建http协议内容,发起http请求
       httpReq := `GET / HTTP/1.0
    Host: www.findme.wang
    User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36
    Content-Type:application/x-www-form-urlencoded
    Content-Length:0
    
    `
       _, err = fmt.Fprintf(conn, httpReq)
       if err != nil {
          fmt.Println("http request error:", err)
          return
       }
    
       data, err := ioutil.ReadAll(conn)
       if err != nil {
              fmt.Println("读取err ", err)
       }
       fmt.Println(string(data)) // print res
       fmt.Println("读取的数据长度:", len(data))
    }

    五、补充

    此外,我们还可以使用io包提供的一些方法,比如:io.ReadAtLeast、io.ReadFull等

    1、io.ReadAtLeast

    从输入流中至少min个字节,放到buf中,返回读取的字节数和err,结构如下:

    func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error) {
       if len(buf) < min {
          return 0, ErrShortBuffer
       }
       for n < min && err == nil {
          var nn int
          nn, err = r.Read(buf[n:])
          n += nn
       }
       if n >= min { //读取字节不小于min的时候,把err 设置nil
          err = nil 
       } else if n > 0 && err == EOF {
          err = ErrUnexpectedEOF
       }
       return
    }

    a、如果buf的长度小于 min,会触发ErrShortBuffer 

    b、如果读取的字节数小于min,这会触发ErrUnexpectedEOF 错误

    c、如果读取的字节数不小于min ,就算遇到了err,也会返回nil

    2、io.ReadFull 

    func ReadFull(r Reader, buf []byte) (n int, err error) {
       return ReadAtLeast(r, buf, len(buf))
    }

    io.ReadFull本质上面调用了io.ReadAtLeast就不在说了。


    本文为原创文章,请尊重辛勤劳动,如需转载,请保留本文地址
    http://www.findme.wang/blog/detail/id/601.html

    若您感觉本站文章不错,读后有收获,不妨赞助一下?

    我要赞助

    您还可以分享给朋友哦

    更多
    顶
    0
    踩
    0
    • 上一篇: goland中粘贴后如何保持原来的缩进​
    • 下一篇: 关于go-playground/validator源码分析
    • 查看评论
    • 正在加载中...
    • 留言
    • 亲,您还没有登录,登录后留言不需要审核哦!
      可以使用如下方式登录哦!
  • CSDN | 新浪微博 | github | 关于我们 | 我要留言 | 友链申请
  • 豫ICP备18038193号    Copyright ©lidequan All Rights Reserved