千家信息网

Go中string与[]byte如何高效互转

发表于:2026-11-02 作者:千家信息网编辑
千家信息网最后更新 2026年11月02日,小编给大家分享一下Go中string与[]byte如何高效互转,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!前言当我们使用
千家信息网最后更新 2026年11月02日Go中string与[]byte如何高效互转

小编给大家分享一下Go中string与[]byte如何高效互转,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

前言

当我们使用go进行数据序列化或反序列化操作时,可能经常涉及到字符串和字节数组的转换。例如:

if str, err := json.Marshal(from); err != nil {    panic(err)} else {    return string(str)}

json序列化后为[]byte类型,需要将其转换为字符串类型。当数据量小时,类型间转换的开销可以忽略不计,但当数据量增大后,可能成为性能瓶颈,使用高效的转换方法能减少这方面的开销

数据结构

在了解其如何转换前,需要了解其底层数据结构

本文基于go 1.13.12

string:

type stringStruct struct {   str unsafe.Pointer   len int}

slice:

type slice struct {   array unsafe.Pointer   len   int   cap   int}

与slice的结构相比,string缺少一个表示容量的cap字段,因此不能对string遍历使用内置的cap()函数那为什么string不需要cap字段呢?因为go中string被设计为不可变类型(当然在很多其他语言中也是),由于其不可像slice一样追加元素,也就不需要cap字段判断是否超出底层数组的容量,来决定是否扩容

只有len属性不影响for-range等读取操作,因为for-range操作只根据len决定是否跳出循环

那为什么字符串要设定为不可变呢?因为这样能保证字符串的底层数组不发生改变

举个例子,map中以string为键,如果底层字符数组改变,则计算出的哈希值也会发生变化,这样再从map中定位时就找不到之前的value,因此其不可变特性能避免这种情况发生,string也适合作为map的键。除此之外,不可变特性也能保障数据的线程安全

常规实现

字符串不可变有很多好处,为了维持其不可变特性,字符串和字节数组互转一般是通过数据拷贝的方式实现:

var a string = "hello world"var b []byte = []byte(a)  // string转[]bytea = string(b)             // []byte转string

这种方式实现简单,但是通过底层数据复制实现的,在编译期间分别转换成对slicebytetostring和stringtoslicebyte的函数调用

string转[]byte

func stringtoslicebyte(buf *tmpBuf, s string) []byte {   var b []byte   if buf != nil && len(s) <= len(buf) {      *buf = tmpBuf{}      b = buf[:len(s)]   } else {      // 申请内存      b = rawbyteslice(len(s))   }   // 复制数据   copy(b, s)   return b}

其根据返回值是否逃逸到堆上,以及buf的长度是否足够,判断选择使用buf还是调用rawbyteslice申请一个slice。但不管是哪种,都会执行一次copy拷贝底层数据

[]byte转string

func slicebytetostring(buf *tmpBuf, b []byte) (str string) {   l := len(b)   if l == 0 { return ""   }   if l == 1 {      stringStructOf(&str).str = unsafe.Pointer(&staticbytes[b[0]])      stringStructOf(&str).len = 1      return   }   var p unsafe.Pointer   if buf != nil && len(b) <= len(buf) {      p = unsafe.Pointer(buf)   } else {      p = mallocgc(uintptr(len(b)), nil, false)   }   // 赋值底层指针   stringStructOf(&str).str = p   // 赋值长度   stringStructOf(&str).len = len(b)   // 拷贝数据   memmove(p, (*(*slice)(unsafe.Pointer(&b))).array, uintptr(len(b)))   return}

首先处理长度为0或1的情况,再判断使用buf还是通过mallocgc新申请一段内存,但无论哪种方式,最后都要拷贝数据
这里设置了转换后字符串的len属性

高效实现

如果程序保证不对底层数据进行修改,那么只转换类型,不拷贝数据,是否可以提高性能?

unsafe.Pointer,int,uintpt这三种类型占用的内存大小相同

var v1 unsafe.Pointervar v2 intvar v3 uintptrfmt.Println(unsafe.Sizeof(v1)) // 8fmt.Println(unsafe.Sizeof(v2)) // 8fmt.Println(unsafe.Sizeof(v3)) // 8

因此从底层结构上来看string可以看做[2]uintptr,[]byte切片类型可以看做 [3]uintptr

那么从string转[]byte只需构建出 [3]uintptr{ptr,len,len}

这里我们为slice结构生成了cap字段,其实这里不生成cap字段对读取操作没有影响,但如果要往转换后的slice append元素可能有问题,原因如下:

这样做slice的cap属性是随机的,可能是大于len的值,那么append时就不会新开辟一段内存存放元素,而是在原数组后面追加,如果后面的内存不可写就会panic

[]byte转string更简单,直接转换指针类型即可,忽略cap字段

实现如下:

func stringTobyteSlice(s string) []byte {   tmp1 := (*[2]uintptr)(unsafe.Pointer(&s))   tmp2 := [3]uintptr{tmp1[0], tmp1[1], tmp1[1]}   return *(*[]byte)(unsafe.Pointer(&tmp2))}func byteSliceToString(bytes []byte) string {   return *(*string)(unsafe.Pointer(&bytes))}

这里使用unsafe.Pointer来转换不同类型的指针,没有底层数据的拷贝

性能测试

接下来对高效实现进行性能测试,这里选用长度为100的字符串或字节数组进行转换

分别测试以下4个方法:

func stringTobyteSlice(s string) []byte {   tmp1 := (*[2]uintptr)(unsafe.Pointer(&s))   tmp2 := [3]uintptr{tmp1[0], tmp1[1], tmp1[1]}   return *(*[]byte)(unsafe.Pointer(&tmp2))}func stringTobyteSliceOld(s string) []byte {   return []byte(s)}func byteSliceToString(bytes []byte) string {   return *(*string)(unsafe.Pointer(&bytes))}func byteSliceToStringOld(bytes []byte) string {   return string(bytes)}

测试结果如下:

BenchmarkStringToByteSliceOld-12 28637332 42.0 ns/op

BenchmarkStringToByteSliceNew-12 1000000000 0.496 ns/op

BenchmarkByteSliceToStringOld-12 32595271 36.0 ns/op

BenchmarkByteSliceToStringNew-12 1000000000 0.256 ns/op

可以看出性能差距比较大,如果需要转换的字符串或字节数组长度更长,性能提升更加明显

以上是"Go中string与[]byte如何高效互转"这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注行业资讯频道!

数据 字符 底层 字符串 类型 数组 字段 拷贝 可变 内存 性能 结构 长度 字节 元素 属性 序列 指针 方式 特性 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 山东特亿宝互联网科技有限公司 中关村互联网 科技集团 互联网 的科技创新 礼当家互联网科技 云电互联网科技 奇士达互联网科技 亿账通互联网科技 万达互联网科技 掌上梦互联网科技 湖南洣水互联网科技有限公司扣款 科技与互联网的利与弊 天速互联网络科技有限公司 移动互联网科技股 未来五年的互联网科技 米聚互联网科技南京有限公司排面 互联网科技如何创业 国大互联网教育科技有限公司薪资 中国互联网有什么科技 第三届互联网大会安防黑科技 恒生科技互联网指数十大成分股 南京欣网互联网络科技有限公司 金诺互联网科技 金融科技与互联网金融 互联网科技公司官网 奇酷互联网科技公司 互联网科技类展会 注册互联网科技公司经营范围大全 国大互联网教育科技有限公司工作 锡山区互联网智能科技产品供应 伍佰万互联网科技是干嘛的
0