io.Reader 解析
简介
io.Reader 是一个 Interface 类型,功能非常强大,在任何需要读的地方我们都尽量使用它。先来看下它的原型:
type Reader interface {
Read(p []byte) (n int, err error)
}
可见,任何实现了 Read() 函数的对象都可以作为 Reader 来使用。
Reader 类型
标准库中有许多不同的 Reader 类型,最常见的就是 bytes, strings 等几个库。我们或多或少都用过它们。下面来看几个最常见的例子:
文件操作
当我们调用 os.Open()
打开一个文件,它会返回一个 os.File
对象,而这个对象其实就是一个 Reader 类型(因为它实现了 Read 函数):
var r io.Reader
var err error
r, err = os.Open("file.txt")
字符串
我们还可以从一个普通的字符串 string 来创建一个 Reader,如下:
var r io.Reader
r = strings.NewReader("Read will return these bytes")
网络 net
net.Conn 是一个 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
}
当然,提供的一个私有类型 conn 是一个 struct,其实现了上述 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) {
...
}
使用场景:
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.0rnrn")
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, "%srn", domain)
return ioutil.ReadAll(conn)
}
http.Request 中的 body 数据就是一个 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
http.Post() 请求
原型:
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"))
bytes.Buffer
是 struct 类型,但同时也是一个 Reader/Writer,因为它实现了 Read() /Writer() 等函数。
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
}
bufio
标准库 bufio
提供了对 io.Reader 和 io.Writer 等 I/O 对象的封装,实现了缓冲的功能。
比如 :
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
例子:
从标准输入读取内容到 buffer 中
reader := bufio.NewReader(os.Stdin)
或者初始化一个 bytes.Buffer
对象,然后据此生成一个 bufio.Reader
对象
readbuffer := bytes.NewBuffer([]byte("input string to be read into new buffer"))
reader := bufio.NewReader(readbuffer)
由于 net.Conn 也是一个 Reader,因此还可以为它来创建一个 bufio.Reader,如下:
listener, err:= net.Listen("tcp", ":8888")
for {
conn, _ := listener.Accept()
...
}
reader := bufio.NewReader(conn)
编解码 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 是一个 interface,声明了 Read() , Write() 等方法, 因此这里很方便的就可以作为 Reader 使用了。
除此之外,GO 语言标准库中还有很多 Reader 类型,我们应该在一切能使用它们的场景下都尽量使用它们。
使用 Reader
上面我们看了一些不同的 Reader 类型,现在我们看一下如何使用它们
直接调用该 Reader 对象的 Read() 函数来读取数据
p := make([]byte, 256)
n, err := r.Read(p)
使用 ioutil.ReadAll 从一个 Reader 中读取数据,返回 raw []byte
b, err := ioutil.ReadAll(r)
使用 io.Copy() 从一个 Reader 中读取数据,并写入一个 Writer
n, err := io.Copy(w, r)
使用 JSON Decoder 从一个 Reader 中直接 decode 数据
err := json.NewDecoder(r).Decode(v)
从一个已经 gzipped 了的 []byte 数据的 Reader 中创建一个 gzip.Reader
r = gzip.NewReader(r)
这样的话,从这个新创建的 reader 中读取数据的话就是已经自动 unzipped 的数据了。
设计 Reader 接口
在编写应用程序接口的时候,如果我们需要接受 []byte 或者 string 等类型的数据,我们可以把参数设置成接受 Reader 类型而不是 []byte 或 string,这样的话,我们的接口就能变得更加通用,适用的场景也会更多。
如下:
func Reverse(s string) (string, error)
可以变成
func Reverse(r io.Reader) io.Reader
这样改变之后,如果别人想要传入一个 string,那么它可以这样:
r = Reverse(strings.NewReader("Make me backwards"))
如果别人想要传入一个文件对象,可以这样:
f, err := os.Open("file.txt")
if err != nil {
log.Fatalln(err)
}
r = Reverse(f)
或者,还可以传入一个 http.Request.Body
func handle(w http.ResponseWriter, r *http.Request) {
rev := Reverse(r.Body)
// etc...
}
最后建议:
在任何需要处理数据流的地方,都应该尽可能使用 Reader/Writer。
- rpc框架之 thrift 学习 1 - 安装 及 hello world
- WordPress自定义栏目运用实例Ⅰ:添加文章来源
- ZooKeeper 笔记(5) ACL(Access Control List)访问控制列表
- 算法:支持重复元素的二分查找
- .NET 异常处理的动作策略(Action Policy)
- 如何在mac本上安装android sdk
- Fityk-曲线拟合工具
- maven: 打包可运行的jar包(java application)及依赖项处理
- Effective java 第2版 - 笔记(01) 单例(Singleton)的枚举(enum)实现
- 恶心的0.5四舍五入问题
- 乐视金融更名 相关域名引关注
- weblogic.nodemanager.common.ConfigException: Native version is enabled but nodemanager native librar
- hadoop 2.6伪分布安装
- ssh 免密码设置失败原因总结
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 使用Apple Configurator 2提取商店ipa or app文件
- Spring 自动装配模式之byType
- 使用ATOMac进行Mac自动化测试
- 【赵渝强老师】什么是Oracle的数据字典?
- antd 如何在 src目录下 引入 Public 目录下的文件
- (精编)Python与安全(三)SSTI服务器模板注入
- 一年经验Java开发0713面试
- 【分享】MicroBlaze大内部存储器(AXI BRAM)设计
- Spring Beans 自动装配
- Python将txt文件内容转换成列表
- 刷题记录-猿辅导2020
- redis stream(一):stream命令入门
- os.path.basename()
- 利用frida与ida实战脱壳360加固
- 你知道C语言中的危险函数吗?