如何在Golang中解包字节缓冲区中的各种整数形式?

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

How do I unpack various form of integers in a byte buffer in Golang?

问题

我需要从字节缓冲区中提取各种字段。我想出了以下解决方案:

  1. func (fs *FileSystem) readSB() {
  2. // fs.f 是一个 *os.File
  3. buf := make([]byte, 1024)
  4. fs.f.ReadAt(buf, 1024)
  5. // 偏移量: 类型
  6. var p *bytes.Buffer
  7. // 0: uint32
  8. p = bytes.NewBuffer(buf[0:])
  9. binary.Read(p, binary.LittleEndian, &fs.sb.inodeCount)
  10. // 4: uint32
  11. p = bytes.NewBuffer(buf[4:])
  12. binary.Read(p, binary.LittleEndian, &fs.sb.blockCount)
  13. // 20: uint32
  14. p = bytes.NewBuffer(buf[20:])
  15. binary.Read(p, binary.LittleEndian, &fs.sb.firstDataBlock)
  16. // 24: uint32
  17. p = bytes.NewBuffer(buf[24:])
  18. binary.Read(p, binary.LittleEndian, &fs.sb.blockSize)
  19. fs.sb.blockSize = 1024 << fs.sb.blockSize
  20. // 32: uint32
  21. p = bytes.NewBuffer(buf[32:])
  22. binary.Read(p, binary.LittleEndian, &fs.sb.blockPerGroup)
  23. // 40: uint32
  24. p = bytes.NewBuffer(buf[40:])
  25. binary.Read(p, binary.LittleEndian, &fs.sb.inodePerBlock)
  26. }

有没有更好/更符合惯用方式/更直接的方法来做到这一点?

  • 我希望保持偏移量明确
  • 我希望尽可能从字节缓冲区中读取,而不是从文件中寻找和读取。
英文:

I need to extract various fields in a byte buffer. I came up with this solution:

  1. func (fs *FileSystem) readSB() {
  2. // fs.f is a *os.File
  3. buf := make([]byte, 1024)
  4. fs.f.ReadAt(buf, 1024)
  5. // Offset: type
  6. var p *bytes.Buffer
  7. // 0: uint32
  8. p = bytes.NewBuffer(buf[0:])
  9. binary.Read(p, binary.LittleEndian, &amp;fs.sb.inodeCount)
  10. // 4: uint32
  11. p = bytes.NewBuffer(buf[4:])
  12. binary.Read(p, binary.LittleEndian, &amp;fs.sb.blockCount)
  13. // 20: uint32
  14. p = bytes.NewBuffer(buf[20:])
  15. binary.Read(p, binary.LittleEndian, &amp;fs.sb.firstDataBlock)
  16. // 24: uint32
  17. p = bytes.NewBuffer(buf[24:])
  18. binary.Read(p, binary.LittleEndian, &amp;fs.sb.blockSize)
  19. fs.sb.blockSize = 1024 &lt;&lt; fs.sb.blockSize
  20. // 32: uint32
  21. p = bytes.NewBuffer(buf[32:])
  22. binary.Read(p, binary.LittleEndian, &amp;fs.sb.blockPerGroup)
  23. // 40: uint32
  24. p = bytes.NewBuffer(buf[40:])
  25. binary.Read(p, binary.LittleEndian, &amp;fs.sb.inodePerBlock)
  26. }

Is there a more better/idiomatic/straightforward way of doing this?

  • I want to keep offsets explicit
  • I want to read from the byte buffer, not seeking and reading from the file when possible.

答案1

得分: 33

你可以通过使用.Next()来跳过不想读取的字节,从而避免每次创建新的缓冲区:

  1. {
  2. // 偏移量: 类型
  3. p := bytes.NewBuffer(buf)
  4. // 0: uint32
  5. binary.Read(p, binary.LittleEndian, &fs.sb.inodeCount)
  6. // 4: uint32
  7. binary.Read(p, binary.LittleEndian, &fs.sb.blockCount)
  8. // 跳过 [8:20)
  9. p.Next(12)
  10. // 20: uint32
  11. binary.Read(p, binary.LittleEndian, &fs.sb.firstDataBlock)
  12. // 24: uint32
  13. binary.Read(p, binary.LittleEndian, &fs.sb.blockSize)
  14. fs.sb.blockSize = 1024 << fs.sb.blockSize
  15. // 跳过 [28:32)
  16. p.Next(4)
  17. // 32: uint32
  18. binary.Read(p, binary.LittleEndian, &fs.sb.blockPerGroup)
  19. // 跳过 [36:40)
  20. p.Next(4)
  21. // 40: uint32
  22. binary.Read(p, binary.LittleEndian, &fs.sb.inodePerBlock)
  23. }

或者你可以避免逐块读取并创建一个头部结构,直接使用binary.Read进行读取:

  1. type Head struct {
  2. InodeCount uint32 // 0:4
  3. BlockCount uint32 // 4:8
  4. Unknown1 uint32 // 8:12
  5. Unknown2 uint32 // 12:16
  6. Unknown3 uint32 // 16:20
  7. FirstBlock uint32 // 20:24
  8. BlockSize uint32 // 24:28
  9. Unknown4 uint32 // 28:32
  10. BlocksPerGroup uint32 // 32:36
  11. Unknown5 uint32 // 36:40
  12. InodesPerBlock uint32 // 40:44
  13. }
  14. func main() {
  15. var header Head
  16. err = binary.Read(file, binary.LittleEndian, &header)
  17. if err != nil {
  18. log.Fatal(err)
  19. }
  20. log.Printf("%#v\n", header)
  21. }
英文:

You could avoid creating a new buffer every time by using .Next() to skip the bytes you don't want to read:

  1. {
  2. // Offset: type
  3. p := bytes.NewBuffer(buf)
  4. // 0: uint32
  5. binary.Read(p, binary.LittleEndian, &amp;fs.sb.inodeCount)
  6. // 4: uint32
  7. binary.Read(p, binary.LittleEndian, &amp;fs.sb.blockCount)
  8. // Skip [8:20)
  9. p.Next(12)
  10. // 20: uint32
  11. binary.Read(p, binary.LittleEndian, &amp;fs.sb.firstDataBlock)
  12. // 24: uint32
  13. binary.Read(p, binary.LittleEndian, &amp;fs.sb.blockSize)
  14. fs.sb.blockSize = 1024 &lt;&lt; fs.sb.blockSize
  15. // Skip [28:32)
  16. p.Next(4)
  17. // 32: uint32
  18. binary.Read(p, binary.LittleEndian, &amp;fs.sb.blockPerGroup)
  19. // Skip [36:40)
  20. p.Next(4)
  21. // 40: uint32
  22. binary.Read(p, binary.LittleEndian, &amp;fs.sb.inodePerBlock)
  23. }

Or you could avoid reading chunk by chunk and create a header structure which you read directly using binary.Read:

  1. type Head struct {
  2. InodeCount uint32 // 0:4
  3. BlockCount uint32 // 4:8
  4. Unknown1 uint32 // 8:12
  5. Unknown2 uint32 // 12:16
  6. Unknown3 uint32 // 16:20
  7. FirstBlock uint32 // 20:24
  8. BlockSize uint32 // 24:28
  9. Unknown4 uint32 // 28:32
  10. BlocksPerGroup uint32 // 32:36
  11. Unknown5 uint32 // 36:40
  12. InodesPerBlock uint32 // 40:44
  13. }
  14. func main() {
  15. var header Head
  16. err = binary.Read(file, binary.LittleEndian, &amp;header)
  17. if err != nil {
  18. log.Fatal(err)
  19. }
  20. log.Printf(&quot;%#v\n&quot;, header)
  21. }

答案2

得分: 1

我有一个名为binpacker的包来处理这些情况

示例

示例数据:

  1. buffer := new(bytes.Buffer)
  2. packer := binpacker.NewPacker(buffer)
  3. unpacker := binpacker.NewUnpacker(buffer)
  4. packer.PushByte(0x01)
  5. packer.PushUint16(math.MaxUint16)

解包:

  1. var val1 byte
  2. var val2 uint16
  3. var err error
  4. val1, err = unpacker.ShiftByte()
  5. val2, err = unpacker.ShiftUint16()

或者

  1. var val1 byte
  2. var val2 uint16
  3. var err error
  4. unpacker.FetchByte(&amp;val1).FetchUint16(&amp;val2)
  5. unpacker.Error() // 确保错误为nil
英文:

I have a package binpacker to handle those situations

example

example data:

  1. buffer := new(bytes.Buffer)
  2. packer := binpacker.NewPacker(buffer)
  3. unpacker := binpacker.NewUnpacker(buffer)
  4. packer.PushByte(0x01)
  5. packer.PushUint16(math.MaxUint16)

unpack it:

  1. var val1 byte
  2. var val2 uint16
  3. var err error
  4. val1, err = unpacker.ShiftByte()
  5. val2, err = unpacker.ShiftUint16()

Or

  1. var val1 byte
  2. var val2 uint16
  3. var err error
  4. unpacker.FetchByte(&amp;val1).FetchUint16(&amp;val2)
  5. unpacker.Error() // Make sure error is nil

huangapple
  • 本文由 发表于 2012年9月11日 03:02:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/12357865.html
匿名

发表评论

匿名网友

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

确定