go基础第七篇:字符串

在go中,所有字符都是utf8编码的,一个英文字符占1个字节,一个中文汉字占3个字节。

假如有一个字符串str,str:="我是koushr"

一、取字节长度:

方法1:

len(str)。是的,len函数处理字符串时,取的是字节数,而不是字符数。

方法2:

利用utf8包的DecodeRuneInString函数,函数的第一个返回值即是字节数。?????

二、取字符长度:

转成rune数组,取数组长度

三、取子字符串:

go内置了一种方法,在字符串后面直接跟中括号,中括号里面用冒号分隔两个数字,第一个数字是起始索引,第二个数字是终止索引,最终截取的字符串包括前面不包括后面,这个截取方法是按照字节截取的,即那两个数字都是字节索引。如果字符串都是英文,那么字节索引就是字符索引,如果包含中文或者特殊字符,那么字节索引就不是字符索引了。

标准做法是,把字符串转成rune数组,然后截取这个数组(数组截取和go内置的字符串截取方式一样,都是后面直接跟中括号。。。),截取时传字符索引即可,把截取到的数组转成字符串即可得到想要的字符串。

示例如下:

func main() {
    str := "我是中国人"
    a := str[1:3]
    fmt.Println(a)

    b := []rune(str)
    c := b[1:3]
    fmt.Println(string(c))
}

四、子串索引

strings包的indexXXX方法,返回的字节索引。

示例如下:

func main() {
    str := "我是中国人"
    a := "是"
    i := strings.Index(str, a)
    fmt.Println(i)
}

上例i的值是3,而不是1。

如果想返回字符索引,则需要我们自定义一个函数,函数处理逻辑是先获取子串的字节索引,然后把这个索引之前的字符串截取出来,转成rune数组,而数组长度就是子串的字符索引。

示例如下:

func main() {
    str := "我是中国人"
    a := "是"
    j := UnicodeIndex(str, a)
    fmt.Println(j)
}

func UnicodeIndex(str, substr string) int {
    // 子串在字符串的字节位置
    result := strings.Index(str, substr)
    if result >= 0 {
        prefix := str[0:result]
        // 将子串之前的字符串转换成[]rune
        rs := []rune(prefix)
        // 获得子串之前的字符串的长度,便是子串在字符串的字符位置
        result = len(rs)
    }
    return result
}

基础概念:

在go中,string本质上是一个字节数组([]byte)。

Unicode和UTF-8的关系:

Unicode是一个编码表,和ASCII是同一种东西。2021年9月公布的Unicode14.0版本已经有14.4w+个字符。基本上每年都会公布一个版本,扩增一些新字符。

UTF-8是一种对Unicode的编码方式,和UTF-16、UTF-16BE、UTF-16LE是同一种东西。UTF-8对一个字符使用1-4个字节进行编码。

字符在编码表中的编号叫做Code Point,中文翻译成码点。

rune的值表示字符的码点。将string转换成rune数组,数组里面其实就是一个个字符对应的码点。因为Unicode已经有14.4w+个字符,所以rune的最大值不能小于14.4w+,而int16最大值是216/2-1即32767,不满足要求,所以rune底层类型至少是int32。rune底层类型正是int32,int32最大值是10.5亿,足够了。