-
博文分类专栏
- Jquery基础教程
-
- 文章:(15)篇
- 阅读:46567
- shell命令
-
- 文章:(42)篇
- 阅读:154240
- Git教程
-
- 文章:(36)篇
- 阅读:234856
- leetCode刷题
-
- 文章:(76)篇
- 阅读:131803
-
如何从io.Reader 中读数据2019-03-25 13:06 阅读(4445) 评论(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就不在说了。