从golang中的缓冲读取器读取特定数量的字节

从golang中的缓冲读取器读取特定数量的字节,go,bufferedreader,peek,Go,Bufferedreader,Peek,我从bufio包中了解到golang的具体功能 func (b *Reader) Peek(n int) ([]byte, error) Peek返回下一个n字节,而不推进读卡器。字节 在下一次读取调用时停止有效。如果Peek返回的值小于n 字节,它还返回一个错误,解释读取为何短。这个 如果n大于b的缓冲区大小,则错误为ErrBufferFull 我需要能够从读卡器中读取特定数量的字节,将提升读卡器。基本上,与上述功能相同,但它提高了读者的阅读能力。有人知道如何做到这一点吗?将一个n字节大小的

我从bufio包中了解到golang的具体功能

func (b *Reader) Peek(n int) ([]byte, error)
Peek返回下一个n字节,而不推进读卡器。字节 在下一次读取调用时停止有效。如果Peek返回的值小于n 字节,它还返回一个错误,解释读取为何短。这个 如果n大于b的缓冲区大小,则错误为ErrBufferFull


我需要能够从读卡器中读取特定数量的字节,将提升读卡器。基本上,与上述功能相同,但它提高了读者的阅读能力。有人知道如何做到这一点吗?

将一个n字节大小的缓冲区传递给读卡器

func (b *Reader) Read(p []byte) (n int, err error)


读取的字节数将限制为
len(p)

注意
bufio.read
方法最多调用一次底层
io.read
,这意味着它可以返回
n
,而不到达EOF。如果您想准确读取
len(p)
字节或因错误而失败,可以像这样使用
io.ReadFull

n, err := io.ReadFull(reader, p)
即使读卡器是缓冲的,这也能起作用。

我更喜欢Read(),尤其是当您要读取任何类型的文件时,而且它在发送数据块时也很有用,下面是一个示例,说明如何使用它

fs, err := os.Open("fileName"); 

if err != nil{
    fmt.Println("error reading file")
    return
}

defer fs.Close()

reader := bufio.NewReader(fs)

buf := make([]byte, 1024)

for{
    v, _ := reader.Read(buf) //ReadString and ReadLine() also applicable or alternative

    if v == 0{
        return
    }
    //in case it is a string file, you could check its content here...
    fmt.Print(string(buf))
}

为此,您只需要创建一个字节片,并使用

n := 512
buff := make([]byte, n)
fs.Read(buff)  // fs is your reader. Can be like this fs, _ := os.Open('file')

func(b*读取器)读取(p[]字节)(n int,err error)

将数据读入p。它返回读入p的字节数。 这些字节最多取自底层读取器上的一次读取, 因此n可能小于len(p)

TLDR:

完整答案:

@monicuta提到了io.ReadFull,它非常有效。这里我提供另一种方法。它通过将
ioutil.ReadAll
io.LimitReader
链接在一起工作。让我们先看一下文件:

因此,如果您想从
myReader
获取42个字节,您可以这样做

import (
        "io"
        "io/ioutil"
)

func main() {
        // myReader := ...
        my42bytes, err := ioutil.ReadAll(io.LimitReader(myReader, 42))
        if err != nil {
                panic(err)
        }
        //...
}
下面是与io.ReadFull相当的代码

io.ReadFull
相比,优点是不需要手动生成
buf
,其中
len(buf)
是要读取的字节数,然后在读取时将
buf
作为参数传递


相反,您只需告诉
io.LimitReader
您希望从
myReader
读取最多42个字节,然后调用
ioutil.ReadAll
读取所有字节,并将结果作为字节片返回。如果成功,返回的片保证长度为42。

这不会“始终”读取特定数量的字节,但它只会将读取的字节限制为len(p)。此外,它可能根本不会读取。根据此解决方案,您可能需要反复调用
Read
,直到获得预期的数据。并非所有的读者都是一样的。这个答案假设他们是。这应该是公认的答案。消除了“短”读取的烦人疑虑,即无需循环和检查io.EOF等。文档也有一个很好的例子:也许值得注意的是io.ReadFull只是此调用的包装:
io.ReadAtLeast(reader,p,len(p))
同样在
io.ReadFull
的情况下,您应该首先定义
p
,其长度等于您要读取的字节大小,但对于
io.read,至少
p
的长度可以是任何长度,只要它大于或等于您要读取的大小。@sepher是正确的。问题的措辞是“具体的字节数”,即如果我理解英语的权利,这等于“确切的字节数”。此外,如果要在流上实现消息帧,这是一个相当实际的问题。“精确”或“具体”与“大于或等于”不是一回事。如果我需要正好4个字节,那么如果它将读取400呢?实际上,io.LimitReader只有在您想限制一些不受控制的代码时才有用,例如一些外部库代码。如果它是您的代码,那么它就没有任何用处,因为它只调用一次普通的Read(),而普通的Read()无论如何不能读取超过您请求的内容。现在人们在问这个问题,我试图找到一个答案:到底是哪里告诉读者不能阅读超过要求的内容?答案是:没有。在Linux文档中,底层称为“通常”和“预期”。有什么事就不要说一句话。也许,对于谷歌的人来说,这太明显了?我的意思是,在其他地方,人们建议使用LimitReader来限制读取的最大字节数。事实上,这只是一个冗余的安全网,因为io.ReadFull()实际上完成了这项工作,并准确读取了您请求的数字。尽管如此,如果您查看源代码,您将受到怀疑的感染,正如上面@sepher所指出的。))我错过的是Read()文档中的一些提示,说它实际上不能读取超过请求的内容。关于您的最后一段,不能保证该片段的长度为42字节。io读取器返回的字节数可能少于请求的字节数。ReadFull将尝试读取所需的字节数,LimitReader将限制要读取的字节数。ReadFull简化了当您想要真正读取N字节时的处理过程……否则它将返回一个错误(一件好事)。+ReadAll已经可以生成比您需要的更大的缓冲区。这必须是一种可接受的方法。也许在结尾稍微重写一下会有所帮助,但除此之外,它解决了问题并回答了问题。我想知道为什么没有直接的函数。感谢@navigaidActually,io.LimitReader仅在您想要限制某些无法控制的代码(例如某些外部库代码)时才有用。如果它是您的代码,那么它就没有任何用处,因为它只调用一次普通的Read(),而普通的Read()无论如何不能读取超过您请求的内容。现在,人们都在问这个问题,我试图找到一个答案:它到底是在哪里被告知的
$ go doc ioutil.ReadAll
func ReadAll(r io.Reader) ([]byte, error)
     ReadAll reads from r until an error or EOF and returns the data it read. A
     successful call returns err == nil, not err == EOF. Because ReadAll is
     defined to read from src until EOF, it does not treat an EOF from Read as an
     error to be reported. 

$ go doc io.LimitReader
func LimitReader(r Reader, n int64) Reader
     LimitReader returns a Reader that reads from r but stops with EOF after n
     bytes. The underlying implementation is a *LimitedReader.
import (
        "io"
        "io/ioutil"
)

func main() {
        // myReader := ...
        my42bytes, err := ioutil.ReadAll(io.LimitReader(myReader, 42))
        if err != nil {
                panic(err)
        }
        //...
}
$ go doc io.ReadFull
func ReadFull(r Reader, buf []byte) (n int, err error)
    ReadFull reads exactly len(buf) bytes from r into buf. It returns the number
    of bytes copied and an error if fewer bytes were read. The error is EOF only
    if no bytes were read. If an EOF happens after reading some but not all the
    bytes, ReadFull returns ErrUnexpectedEOF. On return, n == len(buf) if and
    only if err == nil. If r returns an error having read at least len(buf)
    bytes, the error is dropped.
import (
        "io"
)

func main() {
        // myReader := ...
        buf := make([]byte, 42)
        _, err := io.ReadFull(myReader, buf)
        if err != nil {
                panic(err)
        }
        //...
}