Golang中Int和binary.write的使用疑惑与困惑解析

Golang中Int和binary.write的使用疑惑与困惑解析 我认为我的做法可能不对。

据我理解,int 的大小没有明确定义,Go 似乎沿用了 C 的做法?无论如何,len(x) 返回值的类型大小没有定义,或者至少没有明确到让 binary.Write 不报错。

那么,正确的处理方式是改用 int64 吗?也就是说,如果我想将 len(x) 的返回值写入文件,我应该使用一个临时变量和更多的代码,对吗?这看起来不太优雅。

我不喜欢使用 JSON,不想依赖语言运行时或大量为我解析 JSON 的代码。

有没有更优雅的方法将数据序列化到缓冲区或文件中,而不必为 int 使用 int64?(不使用 golang 的 typeparams 分支?)

非工作代码 试图为我修复此问题:

编辑:哎呀,它确实能工作。我只是太吹毛求疵了,不想继续并修复其他地方的拼写错误。抱歉。

func save_var(file *os.File, x interface{}) {

	switch t := x.(type) {

		case int:
		x = int64(x.(int))
		_ = t

		case custom_type_t:
		x = int64(x.(custom_type_t))
		_ = t
	}

	err := binary.Write(file, binary.LittleEndian, x)
	if err != nil { panic(err) }
}

更优雅的版本:

func load_var(file *os.File, x interface{}) {

	switch t := x.(type) {

		case *int:
		x = (*int64)(unsafe.Pointer((x.(*int))))
		_ = t

		case *custom_type_t:
		x = (*int64)(unsafe.Pointer((x.(*custom_type_t))))
		_ = t
	}

	err := binary.Read(file, binary.LittleEndian, x)
	if err != nil { panic(err) }
}

如果 int64len() 兼容,我会到处使用它,但我现在就像在 C++ 中决定/默认使用 int,然后在 STL 容器中遇到 size_t 一样困惑。表情符号

感谢阅读。


更多关于Golang中Int和binary.write的使用疑惑与困惑解析的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang中Int和binary.write的使用疑惑与困惑解析的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go中处理int类型与binary.Write/binary.Read时确实需要注意类型大小问题。你的理解是正确的:int的大小是平台相关的(32位系统上是32位,64位系统上是64位),而binary包要求明确大小的类型。

正确做法示例

1. 序列化时显式转换

package main

import (
    "encoding/binary"
    "os"
)

func saveLength(file *os.File, data []byte) error {
    // 将int转换为固定大小的int64进行写入
    length := int64(len(data))
    if err := binary.Write(file, binary.LittleEndian, length); err != nil {
        return err
    }
    // 写入实际数据
    _, err := file.Write(data)
    return err
}

func loadLength(file *os.File) ([]byte, error) {
    var length int64
    if err := binary.Read(file, binary.LittleEndian, &length); err != nil {
        return nil, err
    }
    
    data := make([]byte, length)
    _, err := file.Read(data)
    return data, err
}

2. 使用类型安全的包装函数

package main

import (
    "encoding/binary"
    "io"
)

// 写入int值(自动转换为int64)
func WriteInt(w io.Writer, value int) error {
    return binary.Write(w, binary.LittleEndian, int64(value))
}

// 读取int值
func ReadInt(r io.Reader) (int, error) {
    var v int64
    if err := binary.Read(r, binary.LittleEndian, &v); err != nil {
        return 0, err
    }
    return int(v), nil
}

// 使用示例
func exampleUsage(w io.Writer, data []byte) error {
    // 写入长度
    if err := WriteInt(w, len(data)); err != nil {
        return err
    }
    // 写入数据
    _, err := w.Write(data)
    return err
}

3. 处理自定义类型

package main

import (
    "encoding/binary"
    "fmt"
)

type CustomType int

func (c CustomType) MarshalBinary() ([]byte, error) {
    buf := make([]byte, 8)
    binary.LittleEndian.PutUint64(buf, uint64(c))
    return buf, nil
}

func (c *CustomType) UnmarshalBinary(data []byte) error {
    if len(data) < 8 {
        return fmt.Errorf("insufficient data")
    }
    *c = CustomType(binary.LittleEndian.Uint64(data))
    return nil
}

// 使用binary.Write会自动调用MarshalBinary
func saveCustomType(w io.Writer, c CustomType) error {
    return binary.Write(w, binary.LittleEndian, c)
}

关键点

  1. 不要使用unsafe.Pointer进行类型转换,这会导致未定义行为,特别是当intint64大小不同时(32位系统上)。

  2. 推荐始终使用显式转换

// 写入时
length := int64(len(data))
binary.Write(writer, binary.LittleEndian, length)

// 读取时
var length int64
binary.Read(reader, binary.LittleEndian, &length)
dataLength := int(length)
  1. 对于需要序列化的长度字段,建议统一使用int64,这样可以确保跨平台兼容性。

  2. 如果你需要处理大量数据且关心性能,可以直接使用binary.PutVarintbinary.ReadVarint来处理变长整数编码。

这种显式转换虽然看起来不够简洁,但确保了代码的清晰性和跨平台兼容性,这是Go语言设计哲学的一部分。

回到顶部