参考
1. 问题引出及现象
刚由python转go,刷leetcode每日一题时,按照python的习惯写出来下面这段代码,结果报错:
func minimumSwap(s1 string, s2 string) int {
var diffA, diffB int
for idx, c := range s1{
if c!=s2[idx]{
if c=='x'{
diffA++
}else{
diffB++
}
}
}
if (diffA+diffB) %2 ==1{
return -1
}else{
return diffA/2 + diffB/2 + diffA%2*2
}
}
invalid operation: c != s2[idx] (mismatched types rune and byte) (solution.go)
2. rune类型
rune
类型是 Go 语言的一种特殊数字类型。
在builtin/builtin.go
文件中,它的定义:type rune = int32
;官方对它的解释是:rune
是类型int32
的别名,在所有方面都等价于它,用来区分字符值跟整数值。使用单引号定义 ,返回采用 UTF-8 编码的 Unicode 码点。Go 语言通过rune
处理中文,支持国际化多语言。
3. go中的字符串
字符串(string)在Go语言和Python中有着一些差别和共同点,具体如下:
- 根据Go语言官方的定义:*In Go, a string is in effect a read-only slice of bytes.*
意思是Go中的字符串是一组只读的字节切片(slice of bytes,关于切片的概念后文会讲到,这里你可以把它理解为Python中的列表),每个字符串都使用一个或多个字节表示(当字符为 ASCII 码表上的字符时占用 1 个字节,比如英文字母,其它字符根据需要占用 2-4 个字节,比如汉语、日语中的汉字、平假名、片假名等)。
- Go中的字符串使用的是UTF-8编码,而Python中的字符串则使用的是Unicode编码(Python3)。
- 综上两点,Go中的纯英文字符串的字节切片长度和它的字母个数完全相等,比如test这个英文单词有4个字母,那么,len(“test”)返回的值也为4(len()函数在Go和Python中的用法一样,都可用来返回字符串的长度,并且返回的值都为整数)。因为英文字母的编码为ASCII,可以用字节表示。 而其他需要用Unicode编码的语言,比如中文字符串,那么它的字节切片长度往往不会等于它的文字个数,比如"测试"这个词语虽然只包含两个汉字,但是len(“测试”)会返回6,因为"测"字和"试"字分别需要用3个字节来表示,总共占用6个字节
而在Python3中,字符串默认就是用Unicode表示,因此在Python3中len(“测试”)会返回2,而不是6。
字符串在底层的表示是由单个字节组成的一个不可修改的字节序列,字节使用UTF-8[1]编码标识Unicode[2]文本。Unicode 文本意味着.go
文件内可以包含世界上的任意语言或字符,该文件在任意系统上打开都不会乱码。UTF-8 是 Unicode 的一种实现方式,是一种针对 Unicode 可变长度的字符编码,它定义了字符串具体以何种方式存储在内存中。UFT-8 使用 1 ~ 4 为每个字符编码。
Go 语言把字符分byte
和rune
两种类型处理。byte
是类型unit8
的别名,用于存放占 1 字节的 ASCII 字符,如英文字符,返回的是字符原始字节。rune
是类型int32
的别名,用于存放多字节字符,如占 3 字节的中文字符,返回的是字符 Unicode 码点值。如下图所示:
4. 字符串的迭代与索引
字符串的长度等于其有多少个byte构成,在索引字符串时,返回的是每个字节而非字符。
但是,如果使用range迭代字符串,返回的是字符(rune, 4字节)而非字节。
示例如下:
package main
import (
"fmt"
"reflect"
)
func main() {
str1 := "asd"
str2 := "你好啊"
fmt.Println("str1的长度是:", len(str1), "类型是:", reflect.TypeOf(str1), " 值是:", reflect.ValueOf(str1))
fmt.Println("str2的长度是:", len(str2), "类型是:", reflect.TypeOf(str1), " 值是:", reflect.ValueOf(str1))
fmt.Println("str1[0]的类型是:", reflect.TypeOf(str1[0]), " 值是:", reflect.ValueOf(str1[0]), " 内容是:", string(str1[0]))
fmt.Println("str2[0]的类型是:", reflect.TypeOf(str2[0]), " 值是:", reflect.ValueOf(str2[0]), " 内容是:", string(str2[0]))
for _, v := range str1 {
fmt.Println("str1的迭代元素是类型是:", reflect.TypeOf(v), " 值是:", reflect.ValueOf(v), " 内容是:", string(v))
}
for _, v := range str2 {
fmt.Println("str2的迭代元素是类型是:", reflect.TypeOf(v), " 值是:", reflect.ValueOf(v), " 内容是:", string(v))
}
}
ningfeng@ningfeng-OptiPlex-7080:~/go/src/test$ go run main.go
str1的长度是: 3 类型是: string 值是: asd
str2的长度是: 9 类型是: string 值是: asd
str1[0]的类型是: uint8 值是: 97 内容是: a
str2[0]的类型是: uint8 值是: 228 内容是: ä
str1的迭代元素是类型是: int32 值是: 97 内容是: a
str1的迭代元素是类型是: int32 值是: 115 内容是: s
str1的迭代元素是类型是: int32 值是: 100 内容是: d
str2的迭代元素是类型是: int32 值是: 20320 内容是: 你
str2的迭代元素是类型是: int32 值是: 22909 内容是: 好
str2的迭代元素是类型是: int32 值是: 21834 内容是: 啊