Go语言rune与字符串转换的密切关系解析
捶捶自己 人气:0介绍rune类型
golang中rune与字符串转换的密切关系
rune
类型是 Go 语言的一种特殊数字类型。在 builtin/builtin.go
文件中,它的定义是type rune = int32
.官方对它是这么解释的:rune
是类型 int32
的别名,在所有方面都等价于它,用来区分字符值跟整数值.使用单引号定义 ,返回采用 UTF-8 编码的 Unicode 码点.Go 语言通过 rune
处理中文,支持国际化多语言。
为什么要给int32取一个别名rune呢,为什么不直接使用int32?
这是因为别名的使用在大型项目重构中作用最为明显,它能解决代码升级或迁移过程中可能存在的类型兼容性问题。在go语言中还有另外一个类型别名也是用来处理字符的,要知道在go语言中可没有char这个类型,因此对于字符的处理就要特别对待。
rune如何处理字符?
rune主要表示字符码点,特别在处理中文方面尤其有效。下面是例子
func main() { s := "我是一个gophers" fmt.Println(len(s)) fmt.Println(len([]rune(s))) } 运行结果: 19 11
通过对比我们可以发现中文字符每个占据3个字节,如果使用byte则需要三个不同的值,而使用rune刚好可以完整表达。
引出golang中关于字符串的说明
- Go 源代码始终为 UTF-8。
- 字符串可以包含任意字节。
- 字符串文字中不包含字节级转义符时字符串始终包含有效的 UTF-8 序列。
- 代表 Unicode 码点的字节序列称为
rune
。 - 在 Go 中不会保证字符串中的字符被规范化。
值得注意的地方
在前面已经提到过,golang中没有char这个概念,有的只是byte(uint8)单个字符的代表。因此要如何实现string呢?这个待会再讲。我们现在主要介绍string遍历过程中的问题。请看下面的例子
func main() { s := "我是一个gophers" for i, v := range s { fmt.Printf("第%d个值,类型为%T结果为:%v\n", i, v, string(v)) } for i, v := range s { fmt.Printf("第%d个值,类型为%T结果为:%v\n", i, v, string(s[i])) } } 运行结果: 第0个值,类型为int32结果为:我 第3个值,类型为int32结果为:是 第6个值,类型为int32结果为:一 第9个值,类型为int32结果为:个 第12个值,类型为int32结果为:g 第13个值,类型为int32结果为:o 第14个值,类型为int32结果为:p 第15个值,类型为int32结果为:h 第16个值,类型为int32结果为:e 第17个值,类型为int32结果为:r 第18个值,类型为int32结果为:s ------------------------------------------- 第0个值,类型为int32结果为:æ 第3个值,类型为int32结果为:æ 第6个值,类型为int32结果为:ä 第9个值,类型为int32结果为:ä 第12个值,类型为int32结果为:g 第13个值,类型为int32结果为:o 第14个值,类型为int32结果为:p 第15个值,类型为int32结果为:h 第16个值,类型为int32结果为:e 第17个值,类型为int32结果为:r 第18个值,类型为int32结果为:s
通过比对和循环我们可以得知,golang中对于string的循环,如果是中文则需要注意他的下标序号是以3
为差值,字符是以1
为差值.通过s[i]
和v
的比对可以知道为什么golang中需要选择rune作为字符串的转换值.如果转化是一个中文的话,一个字节的byte是无法容纳的,而rune解决了这个问题.
介绍string类型
关于string类型,在go标准库builtin中有如下说明:
// string is the set of all strings of 8-bit bytes, conventionally but not // necessarily representing UTF-8-encoded text. A string may be empty, but // not nil. Values of string type are immutable. type string string
也就是说:string是8位字节的集合,通常但不一定代表UTF-8编码的文本.string可以为空,但是不能为nil.string的值是不能改变的. 而在go语言的源码中我们可以看到string的构成:
type stringStruct struct { str unsafe.Pointer len int }
stringStruct
就是string
的一个对象,这个str
就是一个指向字符数组首位的一个指针.len
就是这个字符串的长度.那么这个数组指的是什么呢?其实就是一个[]byte
,string内部就是通过这个字符切片组成的。而这个str就是指向了这个切片的首地址.因此也解释了一个现象,为什么[]byte
中的值可以被修改,而string
中的值无法被修改.也正因如此copy内置函数才可以将string类型复制为[]byte类型.
字符串的值不能被更改,但可以被替换. string在底层都是结构体stringStruct{str: str_point, len: str_len},string结构体的str指针指向的是一个字符常量的地址,这个地址里面的内容是不可以被改变的,因为它是只读的,但是这个指针可以指向不同的地址.
为什么替换字符时string比[]byte效率慢?
由于每次替换string
的值时,string
的str
指针无法进行改变指向,因此程序内部就需要去新开辟一个指针指向新的[]byte
,然后先前分配的string
内存就会被GC所回收.这是导致string相较于[]byte操作低效的根本原因.
思考string、byte、rune之间的关系
Go语言把字符分 byte
和 rune
两种类型处理.byte
是类型 unit8
的别名,用于存放占 1 字节的 ASCII 字符,如英文字符,返回的是字符原始字节.rune
是类型 int32
的别名,用于存放多字节字符,如占 3 字节的中文字符,返回的是字符 Unicode 码点值.
总结
- string无法进行修改,只能进行替换
- string底层由一个指针指向[]byte
- rune主要用来表示码点并处理大于1字节小于4个字节的特殊字符
- 中文字符每个码点占据多个字节,并且在string循环中通过rune表示
加载全部内容