Golang公共API中接收数据源枚举器的最常用惯用方法

Golang公共API中接收数据源枚举器的最常用惯用方法 在C#中,通常会使用IEnumerator接口,它允许调用者提供数组、列表或更奇特的数据源(例如TCP连接的内容)等任何形式的数据。 我见过io.Reader接口,并且希望有人能告诉我,接受io.Reader是否是最符合Go语言习惯的方式,以便能够从尽可能多的数据源中接收数据,同时为API用户带来最少的麻烦。

显然,这个接口是基于字节级别的,如果使用基于字节的接口,对于非字节数据(例如uint32),需要适当的限制或转换函数。

我找到的替代方案包括:创建一个可能独特的枚举器类型,其形式可以是Go Cookbook [1]中列出的可能性之一;或者使用现有的包,例如go-enumerate [2],但我的印象是它并不特别流行。另一个选择是使用io.ReadSeeker,因为只有在对象生成成功时,它才允许读取位置向前移动,但这可能会限制调用者的设计自由度。

我考虑的使用场景是一个公共API函数,该函数将从数据源读取数据以生成一个对象,它会消耗数据源中的部分数据,并将多余的数据保留为未读状态,以供下一次公共API调用使用。

如果有人对这类输入数据源参数类型的最常见选择有见解,那么如果您能与我分享,我将非常高兴。

[1] https://github.com/kjk/go-cookbook/tree/master/3-ways-to-iterate [2] https://github.com/swdyh/go-enumerable


更多关于Golang公共API中接收数据源枚举器的最常用惯用方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

由于论坛限制,单独列出:

[3] https://golang.org/pkg/math/ [4] https://golang.org/pkg/encoding/binary/

更多关于Golang公共API中接收数据源枚举器的最常用惯用方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我注意到 bufio [1] 允许通过 io.Readerbufio.Reader(支持窥视)读取字节切片,而 bytes 包 [2] 提供了功能更丰富的 io.Reader/io.ByteReader/io.Writer 缓冲区类型。math 包 [3] 包含浮点数与整数类型之间的转换,而 binary 包 [4] 则使用 io.ByteReaderio.Writer 来处理流。

显然,需要支持的可能性比我原先希望的要多得多。

[1] https://golang.org/pkg/bufio/ [2] https://golang.org/pkg/bytes/

在Go中,处理数据源枚举的惯用方法是使用io.Reader接口。它被广泛采用,能够统一处理文件、网络连接、内存缓冲区等多种数据源。对于你的场景——从数据源读取部分数据并保留未读数据供后续使用——io.Reader是标准选择。

以下是一个示例,展示如何通过io.Reader读取部分数据(例如uint32),同时保持未读数据完整:

package main

import (
    "encoding/binary"
    "errors"
    "io"
)

// 从Reader读取一个uint32,并返回剩余未读的Reader
func ReadUint32(r io.Reader) (uint32, io.Reader, error) {
    // 使用io.LimitReader确保只读取4字节
    limitedReader := io.LimitReader(r, 4)
    buf := make([]byte, 4)
    
    n, err := io.ReadFull(limitedReader, buf)
    if err != nil {
        return 0, r, err
    }
    if n != 4 {
        return 0, r, errors.New("insufficient data for uint32")
    }
    
    value := binary.BigEndian.Uint32(buf)
    
    // 返回剩余数据的Reader
    remainingReader := struct {
        io.Reader
    }{
        Reader: r,
    }
    
    return value, remainingReader.Reader, nil
}

// 使用示例
func main() {
    data := []byte{0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04}
    reader := bytes.NewReader(data)
    
    // 第一次读取
    val1, remainingReader, err := ReadUint32(reader)
    if err != nil {
        panic(err)
    }
    fmt.Printf("Read value: %d\n", val1)
    
    // 从剩余数据中读取下一个字节
    var nextByte byte
    err = binary.Read(remainingReader, binary.BigEndian, &nextByte)
    if err != nil {
        panic(err)
    }
    fmt.Printf("Next byte: %d\n", nextByte)
}

对于更复杂的数据类型,可以结合binary.Read或自定义解码逻辑:

func ReadCustomStruct(r io.Reader) (CustomStruct, io.Reader, error) {
    var result CustomStruct
    // 使用teeReader保留已读数据
    var buf bytes.Buffer
    teeReader := io.TeeReader(r, &buf)
    
    err := binary.Read(teeReader, binary.BigEndian, &result)
    if err != nil {
        return result, r, err
    }
    
    // 计算剩余数据
    remainingReader := io.MultiReader(&buf, r)
    return result, remainingReader, nil
}

如果你的API需要随机访问或回溯能力,可以考虑io.ReadSeeker

func ParseFromSeeker(rs io.ReadSeeker) error {
    // 记录起始位置
    startPos, _ := rs.Seek(0, io.SeekCurrent)
    
    // 读取数据...
    
    // 必要时回溯
    rs.Seek(startPos, io.SeekStart)
    return nil
}

在Go生态中,io.Reader是处理流式数据源的事实标准。它被encoding/jsonencoding/xmlcompress/gzip等标准库包广泛使用。对于你的公共API,接受io.Reader能为调用者提供最大灵活性,同时保持接口简洁。

回到顶部