Читайте ровно n байтов, разве что EOF? - PullRequest
0 голосов
/ 06 сентября 2018

Я использую функцию, которая возвращает io.Reader для загрузки файла из Интернета.

Я хочу обработать файл ровно 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 байтами, состоит в том, что файл EOF например, после 500 байтов. Однако в любом другом случае ErrUnexpectedEOF должен быть возвращен, поскольку возникла проблема.

Что я мог сделать, чтобы достичь этого?

Другая проблема состоит в том, что чтение только 2048 байтов в то время непосредственно из сети, по-видимому, будет иметь большие издержки, если бы я мог получить 256 КБ из сети в буфер, а затем вместо этого взять 2048 байтов, которые мне нужны, из этого буфера, что было бы лучше.

1 Ответ

0 голосов
/ 06 сентября 2018

Например,

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
    }
}
...