我正在尝试在io.Reader上实现代理模式链转换,以便有效地处理大块字节。
完整示例:https://play.golang.org/p/KhM0VXLq4CO
b := bytes.NewBufferString(text)
t := transformReaderHandler(*b)
readByChunk(t)
type transformReaderHandler bytes.Buffer
func (t transformReaderHandler) Read(p []byte) (n int, err error) {
n, err = (*bytes.Buffer)(&t).Read(p)
//if n > 0 {
// Do Something on the chunk
//}
return
}
您是否有更高效(内存效率,计算效率)的解决方案?
为什么这段代码不起作用?
编辑:@svsd解决方案的实现:https://play.golang.org/p/VUpJcyKLB6D
package main
import (
"io"
"fmt"
"bytes"
)
const text = "Reaaaaally long and complex text to read in chunk"
func main() {
b := bytes.NewBufferString(text)
t := (*transformReaderHandler)(b)
readByChunk(t)
}
type transformReaderHandler bytes.Buffer
func (t *transformReaderHandler) Read(p []byte) (n int, err error) {
n, err = (*bytes.Buffer)(t).Read(p)
if n > 0 {
p[0] = 'X'
}
return
}
func readByChunk(r io.Reader) {
var p = make([]byte, 4)
for {
n, err := r.Read(p)
if err == io.EOF {
break
}
fmt.Println(string(p[:n]))
}
}
下面的代码说“过程需要太长时间”
为什么这段代码不起作用?
在transformReaderHandler.Read()
方法中,您有一个值接收器。这意味着每次调用Read()
时,它都会获得调用它的实例的副本。然后当你调用(*bytes.Buffer)(&t).Read(p)
时,它会修改该实例的内部状态,以便下次读取时,它会在之前读取的点之后读取。
现在因为实例是副本,所以在方法退出并且原始实例保持不变之后将其丢弃。因此,每次调用Read()
时,bytes.Buffer.Read()
只读取前几个字节。为了证明这一点,在调用fmt.Println("n=", n, "err=", err)
之后在readByChunk()
中添加一个语句Read()
。
要快速检查这确实是由于值接收器,您可以使用指针接收器定义transformReaderHandler.Read()
并将t
存储为t = (*transformReaderHandler)(b)
。我会让你看看它做了什么。 (编辑:涉及嵌入的正确解决方案在评论中)
您是否有更高效(内存效率,计算效率)的解决方案?
如果您只是寻找缓冲IO以获得更高效的读取,请查看bufio.NewReader()
。如果这还不够,你可以从中获取灵感并包裹io.Reader
界面而不是包裹在bytes.Buffer
实例上。
你每次在bytes.Buffer
上调用Read
时都会复制transformReaderHandler
值,所以你永远无法进入缓冲区。您必须使用*bytes.Buffer
指针来避免此副本。
在transformReaderHandler
中嵌入缓冲区(或者将其添加为命名字段),因此您可以根据需要调用委托Read
方法。
type transformReaderHandler struct {
*bytes.Buffer
}
func (t *transformReaderHandler) Read(p []byte) (n int, err error) {
n, err = t.Buffer.Read(p)
//if n > 0 {
// Do Something
//}
return
}