io.WriteSeeker和io.ReadSeeker从[]byte或文件中读取

10
我有一个名为"DoSomething"的方法。DoSomething将对二进制源数据执行操作,并写出二进制数据。DoSomething需要具备通用性,以处理[] byte数组或文件句柄用作源和目标。为此,我尝试声明该方法如下:func DoSomething(source *io.ReadSeeker,destination *io.WriteSeeker)
我已经实现了适用于缓冲区的ReadSeeker和WriteSeeker,使用了自己的自定义必需方法(如果有一种自动完成此任务的方法,我也很想知道)。不幸的是,我似乎无法弄清楚如何从文件句柄创建io.ReadSeeker或io.WriteSeeker。我相当确定必须有一些预先准备好的方法来处理这个问题,而不必手动实现它们。这是否可能?
3个回答

14

已经有一个文件实现了这两个功能。你可以像这样做:

package main

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

func main() {
   f, err := os.Open("test.txt")
   if err != nil {
     fmt.Println(err)
   }
   defer f.Close()
   f2, err := os.Create("test2.txt")
   if err != nil {
      fmt.Println(err)
   }
   defer f2.Close()
   DoSomething(f, f2) 
}

func DoSomething(source io.ReadSeeker, destination io.WriteSeeker) {
   io.Copy(destination, source)
}

另外,您无需传递接口的指针,这使得处理它们更加容易。


1
谢谢!那应该很明显。我能否从[]byte缓冲区创建ReadSeeker/WriteSeeker,而无需手动实现所有方法? - shellster
1
我原以为bytes.Buffer包可以处理它,但显然不行。在Google小组中搜索有关seeker和bytes的帖子。所以,如果你真的需要它,你可能需要将其包装在实现Seek的东西中。 - Eve Freeman
4
我写了一个能够完美满足你需求的软件包。新结构由bytes.Buffer支持,并实现了一些接口,例如io.Reader,io.Seeker,io.ReaderAtio.Writer等等。这个软件包的主要目的是在数据不需要持久化时,避免使用文件(临时或非临时)。以下是该软件包的链接:https://github.com/mattetti/filebuffer。 - Matt Aimonetti
1
如果需要 io.ReadSeeker,那么 bytes.NewReader 可能正是你所需要的。 - quant2016

9

如果有其他人需要处理类似的问题,这是我最终得出的结果。虽然不完整,但对于我所需的功能足够了:

package filebuffer

import (
    "bytes"
    "errors"
)

type FileBuffer struct {
    Buffer bytes.Buffer
    Index  int64
}

func NewFileBuffer() FileBuffer {
    return FileBuffer{}
}

func (fbuffer *FileBuffer) Bytes() []byte {
    return fbuffer.Buffer.Bytes()
}

func (fbuffer *FileBuffer) Read(p []byte) (int, error) {
    n, err := bytes.NewBuffer(fbuffer.Buffer.Bytes()[fbuffer.Index:]).Read(p)

    if err == nil {
        if fbuffer.Index+int64(len(p)) < int64(fbuffer.Buffer.Len()) {
            fbuffer.Index += int64(len(p))
        } else {
            fbuffer.Index = int64(fbuffer.Buffer.Len())
        }
    }

    return n, err
}

func (fbuffer *FileBuffer) Write(p []byte) (int, error) {
    n, err := fbuffer.Buffer.Write(p)

    if err == nil {
        fbuffer.Index = int64(fbuffer.Buffer.Len())
    }

    return n, err
}

func (fbuffer *FileBuffer) Seek(offset int64, whence int) (int64, error) {
    var err error
    var Index int64 = 0

    switch whence {
    case 0:
        if offset >= int64(fbuffer.Buffer.Len()) || offset < 0 {
            err = errors.New("Invalid Offset.")
        } else {
            fbuffer.Index = offset
            Index = offset
        }
    default:
        err = errors.New("Unsupported Seek Method.")
    }

    return Index, err
}

你可以像这样使用它:
destination := filebuffer.NewFileBuffer()

source, err := os.Open(pathString)
if err != nil {
    return nil, err
}
defer source.Close()

if _, err := encrypter.Decrypt(source, &destination, password); err != nil {
    return nil, err
}

注意,此功能不支持io.SeekCurrent、io.SeekEnd,也不支持寻找EOF。 - rustyx

0

对于任何需要的人,这里提供了一个io.ReadWriteSeeker的实现,支持所有I/O操作:

import (
    "errors"
    "fmt"
    "io"
)

// Implements io.ReadWriteSeeker for testing purposes.
type FileBuffer struct {
    buffer []byte
    offset int64
}

// Creates new buffer that implements io.ReadWriteSeeker for testing purposes.
func NewFileBuffer(initial []byte) FileBuffer {
    if initial == nil {
        initial = make([]byte, 0, 100)
    }
    return FileBuffer{
        buffer: initial,
        offset: 0,
    }
}

func (fb *FileBuffer) Bytes() []byte {
    return fb.buffer
}

func (fb *FileBuffer) Len() int {
    return len(fb.buffer)
}

func (fb *FileBuffer) Read(b []byte) (int, error) {
    available := len(fb.buffer) - int(fb.offset)
    if available == 0 {
        return 0, io.EOF
    }
    size := len(b)
    if size > available {
        size = available
    }
    copy(b, fb.buffer[fb.offset:fb.offset+int64(size)])
    fb.offset += int64(size)
    return size, nil
}

func (fb *FileBuffer) Write(b []byte) (int, error) {
    copied := copy(fb.buffer[fb.offset:], b)
    if copied < len(b) {
        fb.buffer = append(fb.buffer, b[copied:]...)
    }
    fb.offset += int64(len(b))
    return len(b), nil
}

func (fb *FileBuffer) Seek(offset int64, whence int) (int64, error) {
    var newOffset int64
    switch whence {
    case io.SeekStart:
        newOffset = offset
    case io.SeekCurrent:
        newOffset = fb.offset + offset
    case io.SeekEnd:
        newOffset = int64(len(fb.buffer)) + offset
    default:
        return 0, errors.New("Unknown Seek Method")
    }
    if newOffset > int64(len(fb.buffer)) || newOffset < 0 {
        return 0, fmt.Errorf("Invalid Offset %d", offset)
    }
    fb.offset = newOffset
    return newOffset, nil
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接