Golang中rune和byte的使用与区别

 更新时间:2025年02月17日 09:33:16   作者:李歘歘  
rune和byte都是Go语言中表示单个字符的类型,本文就来介绍一下Golang中rune和byte的使用与区别,具有一定的参考价值,感兴趣的可以了解一下

在Go语言中,runebyte都是表示单个字符的类型,但它们有一些关键的区别。

byte 类型

byte 是 uint8 的别名,即一个 8 位无符号整数,表示一个字节,范围是 0 到 255。

  • byte 用于表示 UTF-8 编码中的 字节,适合处理字节流和 ASCII 字符。

字符占用字节数:

  • ASCII 字符(0-127)占用 1 字节。
  • 常见的字符,如拉丁字母、标点符号,占用 1 字节。
  • 中文等非 ASCII 字符会占用 3 字节。

byte 表示:字符串 "你",它在 Go 中的 UTF-8 编码是 0xE4, 0xBD, 0xA0(十六进制)。

s := "你"
for i := 0; i < len(s); i++ {
    fmt.Printf("byte at index %d: %d\n", i, s[i])
}

输出:

byte at index 0: 228
byte at index 1: 189
byte at index 2: 160

rune 类型

rune 是 int32 的别名,即一个 32 位有符号整数,用于表示一个 Unicode 字符。Go中所有字符(包括 ASCII 和 Unicode 字符)都是以 rune 类型表示的,范围是 0 到 0x10FFFF。

  • rune 用于表示 Unicode 字符,它表示字符的 编码点,适合处理字符操作,尤其是涉及 Unicode 字符(如中文、表情符号等)。

rune 表示:

s := "你"
for _, c := range s {
    fmt.Printf("rune: %c, rune value: %d\n", c, c)
}

输出:

rune: 你, rune value: 20320

这表示 "你" 的 Unicode 编码点(20320,即 0x4F60)被 rune 类型存储。

UTF-8 与 Unicode 的关系

  • Unicode 是字符集,而 UTF-8 是 Unicode 字符集的编码方式之一。Unicode 定义了所有字符的编码点,但它并没有规定字符如何存储和传输。为了实现跨平台和跨语言的兼容,UTF-8 被定义为一种将 Unicode 编码点转换为字节序列的方式。除了 UTF-8 外,还有 UTF-16 和 UTF-32。
  • 联系
    • Unicode 为每个字符分配一个编码点(一个数字)。
    • UTF-8 通过不同长度的字节序列来编码这些 Unicode 编码点,使得它们可以被存储在文件中、传输通过网络、显示在屏幕上等。

byte和rune的主要区别

特性byterune
类型uint8 (8-bit unsigned int)int32 (32-bit signed int)
用途处理 ASCII 或字节数据处理 Unicode 字符
表示范围0 到 2550 到 0x10FFFF
常见应用字节流、ASCII字符Unicode字符(包括多字节字符)
存储大小1 字节4 字节
字符集支持仅支持 ASCII 字符支持所有 Unicode 字符

Go的默认编码方式

Go 字符串默认的编码方式是 UTF-8 。所以默认使用 byte 序列来表示字符串中的每个字符。

具体来说,Go 中的字符串(string 类型)是由 UTF-8 编码的字节序列 组成的。因此:

  • 一个 Go 字符串是由多个字节(byte)组成的,每个字节都是 UTF-8 编码的一个字符。
  • 这些字节遵循 UTF-8 编码,Go 字符串既可以包含 ASCII 字符(这些字符在 UTF-8 中占用 1 个字节),也可以包含多字节的 Unicode 字符(如中文字符,这些字符在 UTF-8 中通常占用 3 个字节)。
s := "a"
fmt.Print("占用字节数:", len(s))
fmt.Printf(";类型:%T  ", s[0])
fmt.Println()
s1 := "你"
fmt.Print("占用字节数:", len(s1))
fmt.Printf(";类型:%T  ", s1[0])

输出:

占用字节数:1;类型:uint8  
占用字节数:3;类型:uint8  

遍历方式

遍历 byte

bytes := []byte(s)可以直接将字符串转为byte,当然也可以遍历:

  • 使用 for i := 0; i < len(s); i++,每次迭代都可以访问字符串中的每个字节。
  • len(s) 返回字符串的 字节数(byte),即字符串中包含的字节总数,而不是字符的数量。对于一个包含多字节字符(如中文字符)的字符串,len(s) 返回的是字符串所占用的字节数。
package main

import "fmt"

func main() {
	s := "你" // 包含中文字符

	// 按字节遍历字符串
	fmt.Println("按字节遍历字符串:")
	for i := 0; i < len(s); i++ {
		fmt.Printf("s[%d] = %v (类型: %T)\n", i, s[i], s[i]) // 输出每个字节的值
	}
}

输出:

按字节遍历字符串:
s[0] = 228 (类型: uint8)
s[1] = 189 (类型: uint8)
s[2] = 160 (类型: uint8)

遍历 rune

runes := []rune(s)可以直接将字符串转为rune,当然也可以遍历:

  • 使用 for _, c := range s 遍历字符串时,Go 会自动将字符串 s 中的每个字符解码成 rune 类型,这样即使字符是多字节的,也能正确处理。

  • range 遍历字符串时,按 字符(rune) 进行迭代。每次迭代返回一个 Unicode 码点(rune) 和该字符在字符串中的索引。对于多字节字符,range 会自动跳过这些字节,按字符来迭代。

package main

import "fmt"

func main() {
	s := "你"

	// len(s) 返回字节数
	fmt.Println("len(s) =", len(s)) // 输出:3,因为“你”是由 3 个字节表示

	// 使用 range 遍历字符串,按字符(rune)遍历
	fmt.Println("使用 range 遍历字符串,按字符(rune)遍历:")
	for i, r := range s {
		fmt.Printf("i = %d, r = %v (类型: %T)\n", i, r, r)
	}
}

输出:

len(s) = 3
使用 range 遍历字符串,按字符(rune)遍历:
i = 0, r = 20320 (类型: int32)

补充

for i := range s 的 s[i] 其实也是byte,但是处理中文时候会存在问题。

  • 当你使用for i := range s 处理英文字符串的时候,可能不会有问题,因为英文字符(ASCII 字符)在 UTF-8 编码中是单字节表示的,所以每个字符正好对应一个字节。

  • 但是如果字符串中包含非英文字符(如中文、表情符号等),它们通常会占用多个字节。在这种情况下。使用for i := range s 就会发现问题,range 会按照字符(rune)进行遍历,统计的数量是字符数(rune)【如下只有1个】,而不是字节数(byte)【一个中文,应该是对应3个字节】。

package main

import "fmt"

func main() {
	s := "你" // 字符串包含中文字符

	// 使用 range 遍历字符串
	fmt.Println("使用 range 遍历字符串:")
	for i := range s {
		fmt.Printf("s[%d] = %v (类型: %T)\n", i, s[i], s[i]) // 打印每个字节的值
	}
}

输出:

使用 range 遍历字符串:
s[0] = 228 (类型: uint8)

字符还原

要从 byte 序列或 rune 序列还原回原始字符串,你可以通过以下方式进行操作:

  • 从 byte 序列还原字符串:可以直接使用 string(byteSlice)
  • 从 rune 序列还原字符串:可以直接使用 string(runeSlice)

从 byte 序列还原字符串

package main

import "fmt"

func main() {
	s := "你好" // 字符串 "你好"

	// 将字符串转换成 rune 切片
	bytes := []byte(s)

	fmt.Println("bytes:", bytes)
	// 将 rune 切片转换回字符串
	s1 := string(bytes)
	fmt.Println("还原的字符串:", s1)
}

bytes: [228 189 160 229 165 189]
还原的字符串: 你好

从 rune 序列还原字符串

package main

import "fmt"

func main() {
	s := "你好" // 字符串 "你好"

	// 将字符串转换成 rune 切片
	runes := []rune(s)

	fmt.Println("runes编码:", runes)
	// 将 rune 切片转换回字符串
	s1 := string(runes)
	fmt.Println("还原的字符串:", s1)
}


runes编码: [20320 22909]
还原的字符串: 你好

到此这篇关于Golang中rune和byte的使用与区别的文章就介绍到这了,更多相关Golang rune和byte内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • go语言检测文件是否存在的方法

    go语言检测文件是否存在的方法

    这篇文章主要介绍了go语言检测文件是否存在的方法,实例分析了Go语言文件操作的相关技巧,需要的朋友可以参考下
    2015-03-03
  • go mayfly开源项目代码结构设计

    go mayfly开源项目代码结构设计

    这篇文章主要为大家介绍了go mayfly开源项目代码结构设计详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • Go+Redis实现常见限流算法的示例代码

    Go+Redis实现常见限流算法的示例代码

    限流是项目中经常需要使用到的一种工具,一般用于限制用户的请求的频率,也可以避免瞬间流量过大导致系统崩溃,或者稳定消息处理速率。这篇文章主要是使用Go+Redis实现常见的限流算法,需要的可以参考一下
    2023-04-04
  • golang中的单引号转义问题

    golang中的单引号转义问题

    这篇文章主要介绍了golang中的单引号转义问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • Go语言中的错误处理最佳实践详解

    Go语言中的错误处理最佳实践详解

    这篇文章主要为大家详细介绍了Go语言中的错误处理的相关知识,文中的示例代码讲解详细,对我们深入了解Go语言有一定的帮助,需要的可以参考下
    2023-08-08
  • Golang合并yaml文件过程逐步讲解

    Golang合并yaml文件过程逐步讲解

    之前一直从事java开发,习惯了使用yaml文件的格式,尤其是清晰的层次结构、注释,下面这篇文章主要给大家介绍了关于Golang合并yaml文件的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-01-01
  • 在golang中使用cel的用法详解

    在golang中使用cel的用法详解

    CEL 是一种非图灵完备的表达式语言 ,旨在快速、可移植且执行安全,CEL 可以单独使用,也可以嵌入到其他的产品中,本文将给大家介绍一下golang中如何使用cel,需要的朋友可以参考下
    2023-11-11
  • 利用Golang生成整数随机数方法示例

    利用Golang生成整数随机数方法示例

    这篇文章主要介绍了利用Golang生成整数随机数的相关资料,文中给出了详细的介绍和完整的示例代码,相信对大家具有一定的参考价值,需要的朋友们下面来一起看看吧。
    2017-04-04
  • 简单聊聊Golang中defer预计算参数

    简单聊聊Golang中defer预计算参数

    在golang当中defer代码块会在函数调用链表中增加一个函数调用,下面这篇文章主要给大家介绍了关于Golang中defer预计算参数的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-03-03
  • Golang处理parquet文件实战指南

    Golang处理parquet文件实战指南

    这篇文章主要给大家介绍了关于Golang处理parquet文件的相关资料,文中通过实例代码介绍的非常详细,对大家学习或者使用Golang具有一定的参考学习价值,需要的朋友可以参考下
    2023-03-03

最新评论