哈喽,你们好,我是asong
。今天咱们一块儿来看看Go
语言中的rune
数据类型,首先从一道面试题入手,你能很快说出下面这道题的答案吗?
func main() { str := "Golang梦工厂" fmt.Println(len(str)) fmt.Println(len([]rune(str))) }
运行结果是15
和15
仍是15
和9
呢?先思考一下,一会揭晓答案。前端
其实这并非一道面试题,是我在平常开发中遇到的一个问题,当时场景是这样的:后端要对前端传来的字符串作字符校验,产品的需求是限制为200字符,而后我在后端作校验时直接使用len(str) > 200
来作判断,结果出现了bug
,前端字符校验没有超过200
字符,调用后端接口确一直是参数错误,改为使用len([]rune(str)) > 200
成功解决了这个问题。具体缘由咱们在文中揭晓。面试
Unicode
和字符编码在介绍rune
类型以前,咱们仍是要从一些基础知识开始。 ------ Unicode
和字符编码。后端
Unicode
?咱们都知道计算机只能处理数字,若是想要处理文本须要转换为数字才能处理,早些时候,计算机在设计上采用8bit
做为一个byte
,一个byte
表示的最大整数就是255
,想表示更大的整数,就须要更多的byte
。显然,一个字节表示中文,是不够的,至少须要两个字节,并且还不能和ASCII编码冲突,因此,我国制定了GB2312
编码,用来把中文编进去。可是世界上有不少语言,不一样语言制定一个编码,就会不可避免地出现冲突,因此unicode
字符就是来解决这个痛点的。Unicode
把全部语言都统一到一套编码里。总结来讲:"unicode其实就是对字符的一种编码方式,能够理解为一个字符---数字的映射机制,利用一个数字便可表示一个字符。"数组
虽然unicode
把全部语言统一到一套编码里了,可是他却没有规定字符对应的二进制码是如何存储。以汉字“汉”为例,它的 Unicode
码点是 0x6c49
,对应的二进制数是 110110001001001
,二进制数有 15
位,这也就说明了它至少须要 2
个字节来表示。能够想象,在 Unicode
字典中日后的字符可能就须要 3
个字节或者 4
个字节,甚至更多字节来表示了。分布式
这就致使了一些问题,计算机怎么知道你这个 2
个字节表示的是一个字符,而不是分别表示两个字符呢?这里咱们可能会想到,那就取个最大的,假如 Unicode
中最大的字符用 4
字节就能够表示了,那么咱们就将全部的字符都用 4
个字节来表示,不够的就往前面补 0
。这样确实能够解决编码问题,可是却形成了空间的极大浪费,若是是一个英文文档,那文件大小就大出了 3
倍,这显然是没法接受的。函数
因而,为了较好的解决 Unicode
的编码问题, UTF-8
和 UTF-16
两种当前比较流行的编码方式诞生了。UTF-8
是目前互联网上使用最普遍的一种 Unicode
编码方式,它的最大特色就是可变长。它可使用 1 - 4
个字节表示一个字符,根据字符的不一样变换长度。在UTF-8
编码中,一个英文为一个字节,一个中文为三个字节。学习
Go
语言中的字符串先来看一下官方对string
的定义:ui
// 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
的值是不能改变的
说得通俗一点,其实字符串其实是只读的字节切片,对于字符串底层而言就是一个byte
数组,不过这个数组是只读的,不容许修改。spa
写个例子验证一下:
func main() { byte1 := []byte("Hl Asong!") byte1[1] = 'i' str1 := "Hl Asong!" str1[1] = 'i' }
对于byte
的操做是可行,而string
操做会直接报错:
cannot assign to str1[1]
因此说string
修改操做是不容许的,仅仅支持替换操做。
根据前面的分析,咱们也能够得出咱们将字符存储在字符串中时,也就是按字节进行存储的,因此最后存储的实际上是一个数值。
Go
语言的字符串编码上面咱们介绍了字符串的基本概念,接下来咱们看一下Go
语言中的字符串编码是怎样的。
Go
源代码为 UTF-8
编码格式的,源代码中的字符串直接量是 UTF-8
文本。因此Go
语言中字符串是UTF-8
编码格式的。
Go
语言字符串循环Go
语言中字符串可使用range
循环和下标循环。咱们写一个例子,看一下两种方式循环有什么区别:
func main() { str := "Golang梦工厂" for k,v := range str{ fmt.Printf("v type: %T index,val: %v,%v \n",v,k,v) } for i:=0 ; i< len(str) ; i++{ fmt.Printf("v type: %T index,val:%v,%v \n",str[i],i,str[i]) } }
运行结果:
v type: int32 index,val: 0,71 v type: int32 index,val: 1,111 v type: int32 index,val: 2,108 v type: int32 index,val: 3,97 v type: int32 index,val: 4,110 v type: int32 index,val: 5,103 v type: int32 index,val: 6,26790 v type: int32 index,val: 9,24037 v type: int32 index,val: 12,21378 v type: uint8 index,val:0,71 v type: uint8 index,val:1,111 v type: uint8 index,val:2,108 v type: uint8 index,val:3,97 v type: uint8 index,val:4,110 v type: uint8 index,val:5,103 v type: uint8 index,val:6,230 v type: uint8 index,val:7,162 v type: uint8 index,val:8,166 v type: uint8 index,val:9,229 v type: uint8 index,val:10,183 v type: uint8 index,val:11,165 v type: uint8 index,val:12,229 v type: uint8 index,val:13,142 v type: uint8 index,val:14,130
根据运行结果咱们能够得出以下结论:
使用下标遍历获取的是ASCII
字符,而使用Range
遍历获取的是Unicode
字符。
rune
数据类型官方对rune
的定义以下:
// 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
人工翻译:
rune
是int32
的别名,在全部方面都等同于int32
,按照约定,它用于区分字符值和整数值。
说的通俗一点就是rune
一个值表明的就是一个Unicode
字符,由于一个Go
语言中字符串编码为UTF-8
,使用1-4
字节就能够表示一个字符,因此使用int32
类型范围就能够完美适配。
前面说了这么多知识点,确实有点乱了,咱们如今就根据开始的那道题来作一个总结。为了方便查看,在贴一下这道题:
func main() { str := "Golang梦工厂" fmt.Println(len(str)) fmt.Println(len([]rune(str))) }
这道题的正确答案是15
和9
。
具体缘由:
len()
函数是用来获取字符串的字节长度,rune
一个值表明的就是一个Unicode
字符,因此求rune
切片的长度就是字符个数。由于在utf-8
编码中,英文占1
个字节,中文占3
个字节,因此最终结果就是15
和9
。
贴个图,方便理解:
unicode/utf8
库若是你们对rune
的使用不是很明确,能够学习使用一下Go
标准库unicode/utf8
,其中提供了多种关于rune
的使用方法。好比上面这道题,咱们就可使用utf8.RuneCountInString
方法获取字符个数。更多库函数使用方法请自行解锁,本篇就不作过多介绍了。
针对全文,咱们作一个总结:
UTF-8
Go
语言的字符串能够包含任意字节,字符底层是一个只读的byte
数组。Go
语言中字符串能够进行循环,使用下表循环获取的acsii
字符,使用range
循环获取的unicode
字符。Go
语言中提供了rune
类型用来区分字符值和整数值,一个值表明的就是一个Unicode
字符。Go
语言中获取字符串的字节长度使用len()
函数,获取字符串的字符个数使用utf8.RuneCountInString
函数或者转换为rune
切片求其长度,这两种方法均可以达到预期结果。好啦,这篇文章就到这里啦,素质三连(分享、点赞、在看)都是笔者持续创做更多优质内容的动力!
建立了一个Golang学习交流群,欢迎各位大佬们踊跃入群,咱们一块儿学习交流。入群方式:关注公众号获取。更多学习资料请到公众号领取。
我是asong,一名普普统统的程序猿,让咱们一块儿慢慢变强吧。欢迎各位的关注,咱们下期见~~~
推荐往期文章: