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 个字节丢失.


1
0

黯淡_0001

提问者

2018-09-08

//img.mukewang.com/szimg/5b93a22e000106f609620521.jpg这个内

容读取导致的问题不是很懂,麻烦老师再讲解下,谢谢  


(我们用bufio.Reader包装后,针对bufio.Reader的Peek不会影响bufio.Reader的Read,但是bufio.Reader肚子里的那个io.Reader,就没法保证了)是指什么意思?有点强迫症,不理解很难受。。。


1
1
尼克2018
现在明白了吗
2021-01-12
共1条回复

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方法。


1
0

Google资深工程师深度讲解Go语言 由浅入深掌握Go语言

语法+分布式爬虫实战 为转型工程师量身打造

5995 学习 · 1909 问题

查看课程