Создайте io.ReaderAt из io.Reader

#go #interface

#Вперед #интерфейс

Вопрос:

Существует ли реализация io.ReaderAt , которая может быть создана из реализации io.Reader без предварительного чтения в []byte или string ?

Комментарии:

1. Это ограничение означает, что вы не можете выполнить поиск в обратном направлении или перечитать какой-либо раздел для чтения. Это нормально?

2. Эй, Тим, да, это было бы прекрасно. Конечной целью будет SectionReader, поэтому никакого чтения назад и повторного чтения не требуется.

Ответ №1:

Что-то вроде приведенного ниже. Обратите внимание, что bytes.Reader реализует ReadAt(...) метод / функцию: https://golang.org/pkg/bytes/#Reader .ReadAt. Итак, строка bytes.NewReader — это, по сути, то, что вы ищете.

Получение bytes.Reader :

 var ioReader io.Reader
...
buff := bytes.NewBuffer([]byte{})
size, err := io.Copy(buff, ioReader)
if err != nil {
    return err
}

reader := bytes.NewReader(buff.Bytes())
// Do something with `reader`
  

Ответ №2:

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

Вот пример реализации:

 type unbufferedReaderAt struct {
    R io.Reader
    N int64
}

func NewUnbufferedReaderAt(r io.Reader) io.ReaderAt {
    return amp;unbufferedReaderAt{R: r}
}

func (u *unbufferedReaderAt) ReadAt(p []byte, off int64) (n int, err error) {
    if off < u.N {
        return 0, errors.New("invalid offset")
    }
    diff := off - u.N
    written, err := io.CopyN(ioutil.Discard, u.R, diff)
    u.N  = written
    if err != nil {
        return 0, err
    }

    n, err = u.R.Read(p)
    u.N  = int64(n)
    return
}
  

Пример использования:

 s := strings.NewReader("hello world")

var b [5]byte
ura := NewUnbufferedReaderAt(s)
if _, err := ura.ReadAt(b[:], 0); err != nil {
    panic(err)
}
fmt.Printf("%sn", b[:]) // prints "hello"

/*
if _, err := ura.ReadAt(b[:], 0); err != nil {
    panic(err) // panics
}
fmt.Printf("%sn", b[:])
*/

if _, err := ura.ReadAt(b[:], 6); err != nil {
    panic(err)
}
fmt.Printf("%sn", b[:]) // prints "world"
  

Комментарии:

1. Это здорово! Спасибо 🙂

2. 2-й возврат должен быть return written, err вместо return 0, err .