如何從io.Reader 中讀數據

360雲計算 360雲計算 併發

女主宣言app

Go語言以其自己具備的高併發特性,在雲計算開發中,獲得了普遍的應用,也深受廣大開發者的歡迎。可是你們對go語言真的理解了麼?本文做者通過對go語言的多年實踐應用,現對go語言中如何從io.Reader中讀數據進行了詳細介紹,相信對於go語言愛好者有很大的幫助。下來就跟隨做者一塊兒學習下吧。tcp

PS:豐富的一線技術、多元化的表現形式,盡在「360雲計算」,點關注哦!ide

圖片


1高併發

概述學習

開發過程當中,咱們常常從io.Reader中讀取數據。

type Reader interface {ui

   Read(p []byte) (n int, err error)雲計算

}url

  1. 一次最多讀取len(p)長度的數據。
  2. 當讀取遭遇到error或EOF, 會返回已讀取的數據的字節數和error或EOF。
  3. Read方法,不會修改len(p)的大小。
  4. 使用io.EOF 表明結束了。
Talk is cheap. Show me the code ,下面是一個從read讀取的案例:

package mainspa

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循環反覆的讀,有沒有簡潔的方式呢?


2

利用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。


3

使用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))

}



4

補充

此外,咱們還能夠使用io包提供的一些方法,好比:io.ReadAtLeast、io.ReadFull等.一、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

}


  • 若是buf的長度小於 min,會觸發ErrShortBuffer 。

  • 若是讀取的字節數小於min,這會觸發ErrUnexpectedEOF 錯誤。
  • 若是讀取的字節數不小於min ,就算遇到了err,也會返回nil。
二、io.ReadFull 

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

   return ReadAtLeast(r, buf, len(buf))

}

io.ReadFull本質上面調用了io.ReadAtLeast,在此再也不贅述。
相關文章
相關標籤/搜索