问题
I'm writing a library to deal with a binary format.
I have a struct with array vars, that I would like to keep for documentation purposes.
I need also to seek and tell from the input slice of bytes.
Some pseudocode:
type foo struct {
boo [5]byte
coo [3]byte
}
func main() {
// input is a []byte full of datas, read from a file
var bar foo
// Here i need something that writes 5 bytes to bar.foo from input
bar.foo = somefunc(input, numberOfFoo) // ???
// I need also tell() and seek()
input.seek(n)
}
How can I do that with a single function?
回答1:
Operating on byte slice input
You may use the builtin copy() to copy bytes from a source slice into a destination. If you have an array, slice it to obtain a slice, e.g. bar.boo[:]
. To seek, just use a different offset in the source slice, also by reslicing it, e.g. input[startPos:]
.
For example:
input := []byte{1, 2, 3, 4, 5, 0, 0, 8, 9, 10}
var bar foo
copy(bar.boo[:], input)
// Skip 2 bytes, seek to the 8th byte:
input = input[7:]
copy(bar.coo[:], input)
fmt.Printf("%+v", bar)
Output (try it on the Go Playground):
{boo:[1 2 3 4 5] coo:[8 9 10]}
Creating a ReadSeeker
Another option is to wrap your input byte slice into an io.ReadSeeker such as bytes.Reader, then you can read from it.
For example:
input := []byte{1, 2, 3, 4, 5, 0, 0, 8, 9, 10}
r := bytes.NewReader(input)
var bar foo
if _, err := io.ReadFull(r, bar.boo[:]); err != nil {
panic(err)
}
// Skip 2 bytes, seek to the 8th byte:
if _, err := r.Seek(7, io.SeekStart); err != nil {
panic(err)
}
if _, err := io.ReadFull(r, bar.coo[:]); err != nil {
panic(err)
}
fmt.Printf("%+v", bar)
Output is the same, try it on the Go Playground.
Using encoding/binary
Yet another solution would be to use encoding/binary to read your whole struct in one step.
In order to do this, we need to export the fields, and we have to insert an anonymous or blank field that covers the skipped bytes:
type foo struct {
Boo [5]byte
_ [2]byte // don't care
Coo [3]byte
}
Having the above type, we can read all of it in one step like this:
input := []byte{1, 2, 3, 4, 5, 0, 0, 8, 9, 10}
r := bytes.NewReader(input)
var bar foo
if err := binary.Read(r, binary.LittleEndian, &bar); err != nil {
panic(err)
}
fmt.Printf("%+v", bar)
Output is similar, except that it also displays the anonymous field (try it on the Go Playground):
{Boo:[1 2 3 4 5] _:[0 0] Coo:[8 9 10]}
See related answer: Why use arrays instead of slices?
Reading directly from the original file
You mentioned your input
slice is from reading a file. Note that you do not need to read the file prior, as os.File implements io.Reader, even io.ReadSeeker
, which means you can read from it directly, see the Creating a ReadSeeker
section. You can also directly apply the encoding/binary
solution, as we used a reader in that solution too.
来源:https://stackoverflow.com/questions/53429405/how-to-copy-from-slice-to-array-with-seeking-onto-slice