Go语言如何实现将[][]byte转为io.Reader

 更新时间:2025年02月19日 10:24:58   作者:江湖十年  
本文主要介绍了如何在Go语言中实现将[][]byte转换为io.Reader,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

在春节前的某一天,我在 ekit 项目的交流群里看到大明老师发了这样一条消息:

各位大佬,问个小问题,有咩有谁用过 [][]byte 转为 io.Reader 的东西?我以前搞过一次,但是我忘了是我手搓了一个实现,还是用的开源的,还是SDK 自带的

并且大明老师还为此开了一个 issue。

看到这条消息,我想起了我在对 Go 还不太熟悉时,曾写过一个 io.MultiReader 的实现(当时写完了我才知道原来 Go 中自带了 io.MultiReader),想必应该有相似之处,于是就尝试写了一个出来。

不过当我写完时发现已经有人提交了代码,于是我就没把它当回事,也没有写测试代码进行测试,就放一边了。春节假期闲来无事,我忽然想起来这件事,就看了下对应的 pr,发现提交 pr 的作者和我的实现思路不太一样。不过,虽然这个功能很小,既然我也实现了,就补齐下单元测试,发出来供参考,顺便写(水)一篇文章 :)。

思路设计

首先我设计了如下结构体:

type MultiBytes struct {
 data  [][]byte // 存储数据的嵌套切片
 index int      // 当前读/写到的外层切片索引,data[index]
 pos   int      // 当前读/写到的切片所处理到的位置下标,data[index][pos]
}

有了这个结构体,那么就可以设计 MultiBytes 的整体实现思路了。

首先,我们需要一个构造函数 NewMultiBytes 来创建 MultiBytes 对象。其次,则要实现 io.Reader 接口。最后,我们也可以顺便实现一下 io.Write 接口。

MultiBytes 支持的函数和方法设计如下:

type MultiBytes
    func NewMultiBytes(data [][]byte) *MultiBytes
    func (b *MultiBytes) Read(p []byte) (int, error)
    func (b *MultiBytes) Write(p []byte) (int, error)

基于此,我为每个方法画了一个流程图,你可以参考下:

流程图中包含了每个方法内部的主体逻辑。

代码实现

既然有了结构体和方法签名,那么就可以依次实现所有方法了。

首先是构造函数 NewMultiBytes 的实现:

https://github.com/jianghushinian/blog-go-example/blob/main/iox/multi_bytes.go

// NewMultiBytes 构造一个 MultiBytes
func NewMultiBytes(data [][]byte) *MultiBytes {
 return &MultiBytes{
  data: data,
 }
}

这没什么好说的,就是根据给定的 data 初始化了一个 *MultiBytes 对象,index 和 pod 都为默认值 0。

接着是 Read 方法的实现:

// Read 实现 io.Reader 接口,从 data 中读取数据到 p
func (b *MultiBytes) Read(p []byte) (int, error) {
 // 如果 p 是空的,直接返回
 if len(p) == 0 {
   return 0, nil
 }

 // 所有数据都已读完
 if b.index >= len(b.data) {
   return 0, io.EOF
 }

 n := 0// 记录已读取的字节数

 for n < len(p) {
   // 如果当前切片已经读完,则切换到下一个切片
   if b.pos >= len(b.data[b.index]) {
     b.index++
     b.pos = 0
     // 如果所有切片都已读完,退出循环
     if b.index >= len(b.data) {
       break
     }
   }

  // 从当前切片读取数据
  bytes := b.data[b.index]
  cnt := copy(p[n:], bytes[b.pos:])
  b.pos += cnt
  n += cnt
 }

 // 未读取到数据且已经读到结尾
 if n == 0 {
   return 0, io.EOF
 }

 return n, nil
}

Read 方法就是按照流程图中的整体脉络实现的。需要强调的一点是,程序最后还有一个 if n == 0 的判断,如果成立,返回 io.EOF。这是为了处理 data 中嵌套的内部切片为空的情况,比如当 data 值为 [][]byte{[]byte{}} 这种情况时,程序就会走到这个分支。

然后是 Write 方法的实现:

// Write 实现 io.Writer 接口,将数据追加到 data 中
func (b *MultiBytes) Write(p []byte) (int, error) {
 // 如果 p 是空的,直接返回
 if len(p) == 0 {
  return 0, nil
 }

 // 创建副本以避免外部修改影响数据
 clone := make([]byte, len(p))
 copy(clone, p)
 b.data = append(b.data, clone)
 return len(p), nil
}

值得注意的是,在 Write 方法实现中,对 p 进行了拷贝,生成新的副本,目的是防止用户在调用 Write(p) 以后,随意修改 p 的值而影响 MultiBytes 对象内部的 data

最后,如果你不嫌麻烦,还可以增加如下两行代码,以检查 MultiBytes 是否实现了  io.Reader 和 io.Write 接口:

var _ io.Reader = (*MultiBytes)(nil)
var _ io.Writer = (*MultiBytes)(nil)

至此,能够将 [][]byte 转为 io.Reader 的 MultiBytes 实现完成。

我们可以简单测试一下效果。

示例代码:

https://github.com/jianghushinian/blog-go-example/blob/main/iox/examples/multi_bytes.go

package main

import (
"fmt"

"github.com/jianghushinian/blog-go-example/iox"
)

func main() {
 mb := iox.NewMultiBytes([][]byte{[]byte("Hello, World!\n")})
 _, _ = mb.Write([]byte("你好,世界!"))
 p := make([]byte, 32)
 _, _ = mb.Read(p)
 fmt.Println(string(p))
}

执行示例代码,得到输出如下:

$ go run examples/multi_bytes.go
Hello, World!
你好,世界!

总结

本文带大家实现了一个能够将 [][]byte 转为 io.Reader 的 MultiBytes,代码逻辑并不复杂,不过一些细节还是需要注意。

到此这篇关于Go语言如何实现将[][]byte转为io.Reader的文章就介绍到这了,更多相关Go [][]byte转io.Reader内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Go time包AddDate使用解惑实例详解

    Go time包AddDate使用解惑实例详解

    这篇文章主要为大家介绍了Go time包AddDate使用解惑实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • 详解Go语言的context包从放弃到入门

    详解Go语言的context包从放弃到入门

    这篇文章主要介绍了Go语言的context包从放弃到入门,本文通过实例演示给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-12-12
  • Go Web实战之创建项目及增加日志功能

    Go Web实战之创建项目及增加日志功能

    这篇文章主要为大家详细介绍了Go Web项目中如何实现创建项目及增加日志功能,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下
    2022-11-11
  • 浅谈go语言renderer包代码分析

    浅谈go语言renderer包代码分析

    本篇文章主要介绍了浅谈go语言renderer包代码分析,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-05-05
  • 基于Golang实现YOLO目标检测算法

    基于Golang实现YOLO目标检测算法

    目标检测是计算机视觉领域的重要任务,它不仅可以识别图像中的物体,还可以标记出物体的位置和边界框,YOLO是一种先进的目标检测算法,以其高精度和实时性而闻名,本文将介绍如何使用Golang实现YOLO目标检测算法,文中有相关的代码示例供大家参考,需要的朋友可以参考下
    2023-11-11
  • golang中三种线程安全的MAP小结

    golang中三种线程安全的MAP小结

    在Go语言中,Map是并发不安全的,本文主要介绍了golang中三种线程安全的MAP小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-08-08
  • go引入自建包名报错:package XXX is not in std解决办法

    go引入自建包名报错:package XXX is not in std解决办法

    这篇文章主要给大家介绍了go引入自建包名报错:package XXX is not in std的解决办法,这是在写测试引入包名的时候遇到的错误提示,文中将解决办法介绍的非常详细,需要的朋友可以参考下
    2023-12-12
  • golang使用bcrypt包对密码进行加密的方法实现

    golang使用bcrypt包对密码进行加密的方法实现

    本文主要介绍了golang使用bcrypt包对密码进行加密的方法实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • Golang自动追踪GitHub上热门AI项目

    Golang自动追踪GitHub上热门AI项目

    这篇文章主要为大家介绍了Golang自动追踪GitHub上热门AI项目,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • go语言里面实现并发安全扣减库存的几种方式小结

    go语言里面实现并发安全扣减库存的几种方式小结

    本文介绍了GORM框架中的几种高级查询技巧,包括随机返回数据的方法,锁机制的使用,Upsert操作,在gorm-gen中使用原生SQL查询这几种方法,具有一定的参考价值,感兴趣的可以了解一下
    2026-04-04

最新评论