[轉]go語言io reader_如何從io.Reader 中讀數據

 

原文:https://blog.csdn.net/weixin_39605905/article/details/111631303

------------------------

女主宣言

Go語言以其本身具有的高併發特性,在雲計算開發中,得到了廣泛的應用,也深受廣大開發者的歡迎。但是大家對go語言真的理解了麼?本文作者經過對go語言的多年實踐應用,現對go語言中如何從io.Reader中讀數據進行了詳細介紹,相信對於go語言愛好者有很大的幫助。下來就跟隨作者一起學習下吧。

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

 

1

概述

開發過程中,我們經常從io.Reader中讀取數據。
type Reader interface {

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

}

一次最多讀取len(p)長度的數據。
當讀取遭遇到error或EOF, 會返回已讀取的數據的字節數和error或EOF。
Read方法,不會修改len(p)的大小。
使用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循環反覆的讀,有沒有簡潔的方式呢?
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等.1、io.ReadAtLeast從輸入流中至少min個字節,放到buf中,返回讀取的字節數和err,結構如下:
func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error) {

if len(buf)

return 0, ErrShortBuffer

}

for n

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。
2、io.ReadFull
func ReadFull(r Reader, buf []byte) (n int, err error) {

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

}

io.ReadFull本質上面調用了io.ReadAtLeast,在此不再贅述。
360雲計算

由360雲平臺團隊打造的技術分享公衆號,內容涉及數據庫、大數據、微服務、容器、AIOps、IoT等衆多技術領域,通過夯實的技術積累和豐富的一線實戰經驗,爲你帶來最有料的技術分享


————————————————
版權聲明:本文爲CSDN博主「weixin_39605905」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/weixin_39605905/article/details/111631303

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章