golang中String、bytes、rune和字符的概念与应用

发布于:2025-08-19 ⋅ 阅读:(16) ⋅ 点赞:(0)

引入问题-为何打印s[0] 没有打印‘你’字符

package main

import "fmt"

func main() {
	s := "你"
	fmt.Println(s[0])
	fmt.Printf("%s\n", s[0])
}
output
%!s(uint8=228)

首先需要知道go中编码格式和String 类型, Go内置的utf-8编码格式。

utf-8与Unicode

Unicode,全称为Unicode标准(The Unicode Standard),其官方机构Unicode联盟所用的中文名称为统一码,为各种语言中的每个字符定义一个唯一的编码,也描述为码点。
utf-8编码 是一种可变长编码方式,可以用一至四个字节对有效码点进行编码。

String

定义:在Go中,string是一个内置的不可变序列类型,用于表示UTF-8编码的字符串。

内存分配:字符串是不可变的,这意味着一旦创建,其内容就不能被更改。字符串在内存中是不可变的,这意味着每次修改字符串(例如连接或修改)时,都会创建一个新的字符串对象。

用途:主要用于表示文本数据,如用户输入、文件内容、网络消息等。

操作:提供了丰富的字符串操作函数,如len()获取长度、+进行字符串连接、strings包中的各种函数等。

在GO中String类型可以存储任意字节,在编写代码过程,当定义好一个字符串时,存储的是utf-8编码后的字节, 也就是说以下的s和s1字符串是等价的

package main

import "fmt"

func main() {
	s := "你"
	s1 := "\xe4\xbd\xa0"
	if s == s1 {
		fmt.Println("s == s1")
	}
}

可以看到 s字符串实际存储的是对‘你’字符utf-8编码后的字节,那么回到最开始的问题,当使用下标进行访问的时候,s[0]打印的应该是编码后的第一个字节

fmt.Printf("%x", s[0])

output:

e4 //确实如猜测一样打印第一个字节

那么是如何解决这个问题,按字符来进行访问,而不是字节?

rune类型

定义

在builtin/builtin.go文件中定义描述

// rune is an alias for int32 and is equivalent to int32 in all ways. It is
// used, by convention, to distinguish character values from integer values.
type rune = int32

所以 type和int32是等价的
主要作用是用来区分字符值和整数值

Go语言中的rune类型是int32的别名,用于表示单个Unicode字符(码点),支持多字节字符处理(如中文),是处理国际化文本的关键数据类型。
UTF-8 下:1个中文字符占 3个字节

var r rune = '你'
fmt.Println(r)         // 输出 Unicode 编码:20320
fmt.Printf("%c\n", r)  // 输出字符:你

应用

将字符串转化为rune类型

package main

import "fmt"

func main() {
	s := "你好"
	s1 := []rune(s)
	fmt.Printf("字节个数: %d\n", len(s))
	fmt.Printf("字符个数: %d\n", len(s1))
}

output

字节个数: 6
字符个数: 2

range

因为在GO中utf-8是唯一的编码方式,因此在对字符串使用range遍历时,会对字符串使用utf-8进行解码,转化为rune类型输出

package main

import "fmt"

func main() {
	s := "你好"
	for _, c := range s {
		fmt.Printf("%s\n", string(c))
	}
}

output

你
好

for...range 会自动把 string 拆成 rune 单位,而不是 byte!

其他

var r rune = '你'
fmt.Println(r)         // 输出 Unicode 编码:20320
fmt.Printf("%c\n", r)  // 输出字符:你

字符串转换为 rune 切片

str := "你好Go"
runes := []rune(str)
fmt.Println(len(str))    // 字节数(UTF-8):6
fmt.Println(len(runes))  // 字符数(rune):3
fmt.Println(string(runes)) // 还能转回字符串

rune 的实战应用场景
✅ 字符统计(处理中文、emoji 不出错)

✅ 字符串反转(不能直接对 byte 做反转)

✅ 正确截取字符串中的“字符”

✅ 自定义解码器或语法分析器(处理 code point)

func reverse(s string) string {
    runes := []rune(s)
    for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
        runes[i], runes[j] = runes[j], runes[i]
    }
    return string(runes)
}
fmt.Println(reverse("你好Go!")) // !oG好你

byte

rune可表示1~4字节的字符(如中文、日文等),而bytes仅能处理单字节ASCII字符
rune占用4字节,若仅处理英文文本,byte更高效。‌‌

特性 byte rune
本质类型 uint8(1 字节) int32(4 字节)
用于表示 ASCII/单字节字符 Unicode 字符(多字节)
示例字符 ‘A’(一个字节) ‘你’(三个字节)
用途 处理英文、底层IO更快、提供一系列处理字符api 处理中文(比如字符数)、emoji安全

Byte 切片([]byte)
定义:[]byte是一个字节切片,是[]uint8的别名,用于表示一个字节序列。

byte的优点——主要是相比string

  • 内存分配:字节切片是可变的,这意味着你可以修改切片中的元素。如果你需要频繁修改字符串内容,使用字节切片会更高效,因为它不需要每次都创建一个新的字符串对象。

  • 用途:主要用于需要频繁修改文本内容的场景,如处理二进制数据、大文件的读写、需要频繁修改字符串内容的场景等。

  • 操作:提供了基本的切片操作,如len()获取长度、append()添加元素等。对于复杂的文本处理,通常会结合使用bytes包中的函数。

  • 相比rune更适合处理英文数据

相比rune,byte的缺点

  • 字符串通过rune转换后可准确统计字符数(非字节数)
  • 需使用rune切片而非byte切片操作含非ASCII字符的字符串。‌‌
  • 通过string(r)可将rune转为字符串,反之通过rune(s)

网站公告

今日签到

点亮在社区的每一天
去签到