簡(jiǎn)介
io.Reader 是一個(gè) Interface 類型,功能非常強(qiáng)大,在任何需要讀的地方我們都盡量使用它。先來看下它的原型:
type Reader interface {
Read(p []byte) (n int, err error)
}
可見,任何實(shí)現(xiàn)了 Read() 函數(shù)的對(duì)象都可以作為 Reader 來使用姜凄。
Reader 類型
標(biāo)準(zhǔn)庫中有許多不同的 Reader 類型政溃,最常見的就是 bytes, strings 等幾個(gè)庫。我們或多或少都用過它們态秧。下面來看幾個(gè)最常見的例子:
(1) 文件操作
當(dāng)我們調(diào)用 os.Open()
打開一個(gè)文件董虱,它會(huì)返回一個(gè) os.File
對(duì)象,而這個(gè)對(duì)象其實(shí)就是一個(gè) Reader 類型(因?yàn)樗鼘?shí)現(xiàn)了 Read 函數(shù)):
var r io.Reader
var err error
r, err = os.Open("file.txt")
(2)字符串
我們還可以從一個(gè)普通的字符串 string 來創(chuàng)建一個(gè) Reader申鱼,如下:
var r io.Reader
r = strings.NewReader("Read will return these bytes")
(3)網(wǎng)絡(luò) net
<1> net.Conn 是一個(gè) Reader/Writer
原型:
type Conn interface {
Read(b []byte) (n int, err error)
Write(b []byte) (n int, err error)
Close() error
LocalAddr() Addr
RemoteAddr() Addr
SetDeadline(t time.Time) error
SetReadDeadline(t time.Time) error
SetWriteDeadline(t time.Time) error
}
當(dāng)然愤诱,提供的一個(gè)私有類型 conn 是一個(gè) struct,其實(shí)現(xiàn)了上述 interface捐友。
type conn struct {
fd *netFD
}
func (c *conn) Read(b []byte) (int, error) {
...
}
// Write implements the Conn Write method.
func (c *conn) Write(b []byte) (int, error) {
...
}
使用場(chǎng)景:
conn, err := net.Dial("tcp", "google.com:80")
tmp := make([]byte, 256) // using small tmo buffer for demonstrating
for {
n, err := conn.Read(tmp)
if err != nil {
if err != io.EOF {
fmt.Println("read error:", err)
}
break
}
buf = append(buf, tmp[:n]...)
}
或者
func main() {
conn, err := net.Dial("tcp", "google.com:80")
if err != nil {
fmt.Println("dial error:", err)
return
}
defer conn.Close()
fmt.Fprintf(conn, "GET / HTTP/1.0\r\n\r\n")
var buf bytes.Buffer
io.Copy(&buf, conn)
fmt.Println("total size:", buf.Len())
}
或者
func whois(domain, server string) ([]byte, error) {
conn, err := net.Dial("tcp", server+":43")
if err != nil {
return nil, err
}
defer conn.Close()
fmt.Fprintf(conn, "%s\r\n", domain)
return ioutil.ReadAll(conn)
}
<2> http.Request
中的 body 數(shù)據(jù)就是一個(gè) Reader
type Request struct {
Method string
URL *url.URL
Header Header
// Body is the request's body.
Body io.ReadCloser
Host string
Form url.Values
PostForm url.Values
MultipartForm *multipart.Form
...
}
可以這樣使用:
var r io.Reader
r = request.Body
<3> http.Post()
請(qǐng)求
原型:
func (c *Client) Post(url string, contentType string, body io.Reader) (resp *Response, err error)
用法
requestBytes, _ := json.Marshal(request)
requestBuf := bytes.NewBuffer(requestBytes)
httpResponse, _ := c.client.Post(c.address, "text/json", requestBuf)
responseBytes, _ := ioutil.ReadAll(httpResponse.Body)
response := &types.RPCResponse{}
err = json.Unmarshal(responseBytes, response)
或者
resp, err := http.Post("www.example.com/accept.php", "application/x-www-form-urlencoded", strings.NewReader("name=alex"))
(4) bytes.Buffer
是 struct 類型淫半,但同時(shí)也是一個(gè) Reader/Writer,因?yàn)樗鼘?shí)現(xiàn)了 Read() /Writer() 等函數(shù)匣砖。
type Buffer struct {
buf []byte // contents are the bytes buf[off : len(buf)]
off int // read at &buf[off], write at &buf[len(buf)]
bootstrap [64]byte // memory to hold first slice; helps small buffers avoid allocation.
lastRead readOp // last read operation, so that Unread* can work correctly.
}
func (b *Buffer) Read(p []byte) (n int, err error) {
b.lastRead = opInvalid
if b.off >= len(b.buf) {
// Buffer is empty, reset to recover space.
b.Truncate(0)
if len(p) == 0 {
return
}
return 0, io.EOF
}
n = copy(p, b.buf[b.off:])
b.off += n
if n > 0 {
b.lastRead = opRead
}
return
}
為 []byte 創(chuàng)建一個(gè) Reader
有時(shí)候科吭,我們有一個(gè)字節(jié)序列,但是需要調(diào)用的函數(shù)只接收 Reader猴鲫,那么我們可以通過 bytes.NewReader() 來創(chuàng)建一個(gè) Reader 來傳入其中对人。
bs, err := json.Marshal(data)
if err != nil {
return err
}
return io.Copy(os.Stdout, bytes.NewReader(bs))
(5) bufio
標(biāo)準(zhǔn)庫 bufio
提供了對(duì) io.Reader 和 io.Writer 等 I/O 對(duì)象的封裝,實(shí)現(xiàn)了緩沖的功能拂共。
比如 :
func NewReader(rd io.Reader) *Reader
func NewWriterSize(w io.Writer, size int) *Writer
func NewReadWriter(r *Reader, w *Writer) *ReadWrite
func NewScanner(r io.Reader) *Scanner
例子:
從標(biāo)準(zhǔn)輸入讀取內(nèi)容到 buffer 中
reader := bufio.NewReader(os.Stdin)
或者初始化一個(gè) bytes.Buffer
對(duì)象牺弄,然后據(jù)此生成一個(gè) bufio.Reader
對(duì)象
readbuffer := bytes.NewBuffer([]byte("input string to be read into new buffer"))
reader := bufio.NewReader(readbuffer)
由于 net.Conn 也是一個(gè) Reader,因此還可以為它來創(chuàng)建一個(gè) bufio.Reader宜狐,如下:
listener, err:= net.Listen("tcp", ":8888")
for {
conn, _ := listener.Accept()
...
}
reader := bufio.NewReader(conn)
(6) 編解碼 Decoder
func receive(conn net.Conn) {
dec := json.NewDecoder(conn)
msg := new(Message)
err := dec.Decode(msg)
}
原型
func NewDecoder(r io.Reader) *Decoder {
return &Decoder{r: r}
}
注: 由于 conn 是一個(gè) interface势告,聲明了 Read() , Write() 等方法, 因此這里很方便的就可以作為 Reader 使用了。
除此之外抚恒,GO 語言標(biāo)準(zhǔn)庫中還有很多 Reader 類型培慌,我們應(yīng)該在一切能使用它們的場(chǎng)景下都盡量使用它們。
使用 Reader
上面我們看了一些不同的 Reader 類型柑爸,現(xiàn)在我們看一下如何使用它們
(1)直接調(diào)用該 Reader 對(duì)象的 Read() 函數(shù)來讀取數(shù)據(jù)
p := make([]byte, 256)
n, err := r.Read(p)
(2)使用 ioutil.ReadAll
從一個(gè) Reader 中讀取數(shù)據(jù)吵护,返回 raw []byte
b, err := ioutil.ReadAll(r)
(3)使用 io.Copy()
從一個(gè) Reader 中讀取數(shù)據(jù),并寫入一個(gè) Writer
n, err := io.Copy(w, r)
(4)使用 JSON Decoder 從一個(gè) Reader 中直接 decode 數(shù)據(jù)
err := json.NewDecoder(r).Decode(v)
(5)從一個(gè)已經(jīng) gzipped 了的 []byte 數(shù)據(jù)的 Reader 中創(chuàng)建一個(gè) gzip.Reader
r = gzip.NewReader(r)
這樣的話,從這個(gè)新創(chuàng)建的 reader 中讀取數(shù)據(jù)的話就是已經(jīng)自動(dòng) unzipped 的數(shù)據(jù)了馅而。
設(shè)計(jì) Reader 接口
在編寫應(yīng)用程序接口的時(shí)候祥诽,如果我們需要接受 []byte 或者 string 等類型的數(shù)據(jù),我們可以把參數(shù)設(shè)置成接受 Reader 類型而不是 []byte 或 string瓮恭,這樣的話雄坪,我們的接口就能變得更加通用,適用的場(chǎng)景也會(huì)更多屯蹦。
如下:
func Reverse(s string) (string, error)
可以變成
func Reverse(r io.Reader) io.Reader
這樣改變之后维哈,如果別人想要傳入一個(gè) string,那么它可以這樣:
r = Reverse(strings.NewReader("Make me backwards"))
如果別人想要傳入一個(gè)文件對(duì)象登澜,可以這樣:
f, err := os.Open("file.txt")
if err != nil {
log.Fatalln(err)
}
r = Reverse(f)
或者阔挠,還可以傳入一個(gè) http.Request.Body
func handle(w http.ResponseWriter, r *http.Request) {
rev := Reverse(r.Body)
// etc...
}
最后建議:
在任何需要處理數(shù)據(jù)流的地方,都應(yīng)該盡可能使用 Reader/Writer脑蠕。