使用反射将字节复制到结构字段中

huangapple go评论79阅读模式
英文:

Go copy bytes into struct fields with reflection

问题

如何迭代遍历字节切片并将其分配给结构体的字段?

type s struct {
  f1 []byte
  f2 []byte
  f3 []byte
}

func S() s {
  x := s{}
  x.f1 = make([]byte, 4)
  x.f2 = make([]byte, 2)
  x.f3 = make([]byte, 2)
  return x
}

func main() {
  data := []byte{83, 117, 110, 83, 0, 1, 0, 65}

  Z := S()
  // 从这里开始的伪代码
  i := 0
  fields := []interface{}{&Z.f1, &Z.f2, &Z.f3}
  for _, field := range fields {
    f := *field.(*[]byte)
    copy(f, data[i:i+len(f)])
    i += len(f)
  }
}

期望结果:

  • f1 = [83,117,110,83]
  • f2 = [0,1]
  • f3 = [0,65]

我以前在C/C++中做过类似的操作,但是我不知道如何在Go中实现。我需要将分配函数设计成通用的,因为我将有多个不同的结构体,其中一些可能在数据流中不存在。

理想情况下,我希望传入初始化的结构体,然后我的代码会迭代结构体字段并填充它们。

英文:

How can I iterate over a byte slice and assign them to the fields of a struct?

type s struct {
  f1 []byte
  f2 []byte
  f3 []byte
}

func S s {
  x := s{}
  x.f1 = make([]byte, 4)
  x.f1 = make([]byte, 2)
  x.f1 = make([]byte, 2)
  return x
}

func main() {
  data := []byte{83, 117, 110, 83, 0, 1, 0, 65}

  Z := S()
  //pesudo code from here
  i:= 0
  for field in Z {
    field = data[i:len(field)]
    i += len(field)
  }

Expecting:

  • f1 = [83,117,110,83]
  • f2 = [0,1]
  • f3 = [0,65]

I've done this in C/C++ before but I can't figure out how to do it in Go. I need the assigning function to be generic as I'm going to have several different structs some of which may not exist in the stream.

Ideally I want to pass in the initialized struct and my code would iterate over the struct fields filling them in.

答案1

得分: 1

利用二进制/编码包中的反射代码。

步骤1:将字段声明为数组而不是切片。

type S struct {
  F1 [4]byte
  F2 [2]byte
  F3 [2]byte
}

步骤2:使用binary.Read将数据解码到结构体中。

var s S
data := []byte{83, 117, 110, 83, 0, 1, 0, 65}
err := binary.Read(bytes.NewReader(data), binary.LittleEndian, &s)
if err != nil {
    log.Fatal(err)
}

步骤3:完成!

fmt.Print(s) // 输出 {[83 117 110 83] [0 1] [0 65]}

https://go.dev/play/p/H-e8Lusw0RC

英文:

Leverage the reflection code in the binary/encoding package.

Step 1: Declare the fields as arrays instead of slices.

type S struct {
  F1 [4]byte
  F2 [2]byte
  F3 [2]byte
}

Step 2: Decode the data to the struct using binary.Read

var s S
data := []byte{83, 117, 110, 83, 0, 1, 0, 65}
err := binary.Read(bytes.NewReader(data), binary.LittleEndian, &s)
if err != nil {
	log.Fatal(err)
}

Step 3: Done!

fmt.Print(s) // prints {[83 117 110 83] [0 1] [0 65]}

https://go.dev/play/p/H-e8Lusw0RC

答案2

得分: 0

你可以使用reflect.Copy。与内置的copy类似,它将数据复制到目标位置,直到达到目标位置的长度。确保你需要设置的字段是可导出的。

func main() {
    data := []byte{83, 117, 110, 83, 0, 1, 0, 65}

    z := S{
        F1: make([]byte, 4),
        F2: make([]byte, 2),
        F3: make([]byte, 2),
    }
    SetBytes(&z, data)
    fmt.Println(z) // {[83 117 110 83] [0 1] [0 65]}
}

func SetBytes(dst any, data []byte) {
    v := reflect.ValueOf(dst)
    if v.Kind() != reflect.Ptr {
        panic("dst must be addressable")
    }
    v = v.Elem()

    j := 0
    for i := 0; i < v.NumField(); i++ {
        field := v.Field(i)
        if field.Kind() != reflect.Slice {
            continue
        }
        j += reflect.Copy(v.Field(i), reflect.ValueOf(data[j:]))
    }
}

由于假设data始终为[]byte,你可以直接对其进行子切片。

另外,你还可以使用reflect.Value#Slice

d := reflect.ValueOf(data)
// 然后在后面
j += reflect.Copy(v.Field(i), d.Slice(j, d.Len()))

Playground: https://go.dev/play/p/o1MR1qrW5pL

英文:

You can use reflect.Copy. Like the built-in copy, it copies data into the destination up to its length. Make sure the fields you need to set are exported.

func main() {
	data := []byte{83, 117, 110, 83, 0, 1, 0, 65}

	z := S{
		F1: make([]byte, 4),
		F2: make([]byte, 2),
		F3: make([]byte, 2),
	}
	SetBytes(&amp;z, data)
	fmt.Println(z) // {[83 117 110 83] [0 1] [0 65]}
}

func SetBytes(dst any, data []byte) {
	v := reflect.ValueOf(dst)
	if v.Kind() != reflect.Ptr {
		panic(&quot;dst must be addressable&quot;)
	}
	v = v.Elem()

	j := 0
	for i := 0; i &lt; v.NumField(); i++ {
		field := v.Field(i)
		if field.Kind() != reflect.Slice {
			continue
		}
		j += reflect.Copy(v.Field(i), reflect.ValueOf(data[j:]))
	}
}

Since data is assumed to be always []byte, you can subslice it directly.

Alternatively, you can use reflect.Value#Slice:

d := reflect.ValueOf(data)
// and later
j += reflect.Copy(v.Field(i), d.Slice(j, d.Len()))

Playground: https://go.dev/play/p/o1MR1qrW5pL

huangapple
  • 本文由 发表于 2022年11月21日 06:27:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/74512510.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定