首页 > 解决方案 > 除非EOF,否则准确读取n个字节?

问题描述

我正在使用一个返回一个io.Reader从 Internet 下载文件的函数。

我想以精确的 2048 个块处理文件,直到由于 EOF 而不再可能。

io.ReadFull功能几乎是我想要的:

buf := make([]byte, 2048)

for {
    if _, err := io.ReadFull(reader, buf); err == io.EOF {
        return io.ErrUnexpectedEOF
    } else if err != nil {
        return err
    }

    // Do processing on buf
}

这样做的问题是,并非所有文件都是 2048 字节的倍数,因此最后一个块可能只有 500 个字节,io.ReadFull因此将返回ErrUnexpectedEOF并且最后一个块被丢弃。

总结我想要的函数名称可能是io.ReadFullUnlessLastChunk,因此ErrUnexpectedEOF如果buf不能用 2048 字节填充的原因是文件在例如 500 字节之后是 EOF,则不会返回。但是,在任何其他情况下,ErrUnexpectedEOF应在出现问题时退回。

我能做些什么来做到这一点?

另一个问题是直接从网络读取 2048 字节似乎有很多开销,如果我可以从网络获取 256 KB 到缓冲区,然后从该缓冲区中获取我需要的 2048 字节,那会更好.

标签: gobuffer

解决方案


例如,

package main

import (
    "bufio"
    "fmt"
    "io"
    "os"
)

func readChunks(r io.Reader) error {
    if _, ok := r.(*bufio.Reader); !ok {
        r = bufio.NewReader(r)
    }
    buf := make([]byte, 0, 2048)
    for {
        n, err := io.ReadFull(r, buf[:cap(buf)])
        buf = buf[:n]
        if err != nil {
            if err == io.EOF {
                break
            }
            if err != io.ErrUnexpectedEOF {
                return err
            }
        }

        // Process buf
        fmt.Println(len(buf))

    }
    return nil
}

func main() {
    fName := `test.file`
    f, err := os.Open(fName)
    if err != nil {
        fmt.Println(err)
        return
    }
    defer f.Close()

    err = readChunks(f)
    if err != nil {
        fmt.Println(err)
        return
    }
}

推荐阅读