Peek的疑问
来源:15-6 测试CityListParser

黯淡_0001
2018-09-05
在14-6节中,因为Peek导致读取的内容不全,通过bufio.NewReader包装resp.Body后,再读取就正常了。
我看了Peek实现(https://golang.org/src/bufio/bufio.go),只是截取了指定长度n的字节,并没有移动reader的位置指针,有下面2个疑问:
为什么Peek后会导致后面再读取的数据没有Peek的内容呢?
NewReader包装后为什么就可以?
不理解这里,麻烦老师指导下,谢谢。
3回答
-
慕侠5029079
2020-10-19
我的理解是:
bufio.NewReader(r) 是创建了一个 bufio.Reader 对象(参考 src/bufio/bufio.go 源码):
// Reader implements buffering for an io.Reader object.
type Reader struct {
buf []byte
rd io.Reader // reader provided by the client
r, w int // buf read and write positions
err error
lastByte int // last byte read for UnreadByte; -1 means invalid
lastRuneSize int // size of last rune read for UnreadRune; -1 means invalid
}
把页面请求的 resp.Body 赋值到这个对象里面的 bufio.Reader 对象的 rd 字段。
重点的地方:字段 r , w 的值都是零值。
后面 Peek 函数调用步骤:
b.fill() (src/bufio/bufio.go ) --> b.rd.Read(b.buf[b.w:]) (src/strings/reader.go)
b.rd.Read(b.buf[b.w:]) 源码:
// The zero value for Reader operates like a Reader of an empty string.
type Reader struct {
s string
i int64 // current reading index
prevRune int // index of previous rune; or < 0
}
func (r *Reader) Read(b []byte) (n int, err error) {
if r.i >= int64(len(r.s)) {
return 0, io.EOF
}
r.prevRune = -1
n = copy(b, r.s[r.i:])
r.i += int64(n) // 注意的地方
return
}
当调用完成之后, resp.Body 对象的 i 值会被修改为 1024,resp.Body 的1024个字节内容会被读取到 bufio.Reader 的 buf 字段,并返回 buf 切片。
也就是:
bufio.Reader 对象的 rd 字段(嵌套结构体)的 i 值是 1024。
但是, bufio.Reader 的 r 值还是0 , 只是 w 字段的值变成了 1024(我理解这个w是指从 rd io.Reader 对象读取多少数据写入到 buf )。
最后, ioutil.ReadAll() 读取数据
如果是对 resp.Body ,那么就是从 i = 1024 开始;
如果是对 bufio.Reader ,那么就是从 r = 0 开始(细节可以参考源码);
所以,如果把 resp.Body 通过 bufio.NewReader(r) 转为 bufio.Reader 对象,然后再调用 Peek 的话,是会返回缓存的一个切片。
即使 resp.Body 的读取索引值已经修改为 1024 ,但 bufio.Reader 的 r = 0。
可以参考:https://studygolang.com/articles/4367 的中文解释。
如果是 resp.Body 转换成 bufio.Reader 之后,再从 resp.Body 里面读取数据,就是会出现 1024 个字节丢失.
10 -
黯淡_0001
提问者
2018-09-08
这个内
容读取导致的问题不是很懂,麻烦老师再讲解下,谢谢
(我们用bufio.Reader包装后,针对bufio.Reader的Peek不会影响bufio.Reader的Read,但是bufio.Reader肚子里的那个io.Reader,就没法保证了)是指什么意思?有点强迫症,不理解很难受。。。
112021-01-12 -
ccmouse
2018-09-08
首先从逻辑上理解:io.Reader的Read本身就必须伴随着指针的移动,不然我反复调这个io.Reader的Read都获得相同的内容,显然不对。
那么io.Reader是没有Peek能力的,我(bufio.Reader)为了看1024字节的内容,只有一个办法,把他们读进buffer,然后告诉外面假装我Peek到了哪些内容。而这个原始的io.Reader被读了1024字节后,下一个Read只能从1025字节开始读。
代码上,bufio.go的132行调用了fill,fill里面显然调用了原始的这个io.Reader的Read方法。
10
相似问题