3

Golang string

 2 years ago
source link: https://ray-g.github.io/golang/golang_string/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

字符串的本质

Golang中的string,实际是一个不可变的字节序列,每个字节都是UTF-8编码的文本。 虽然每一个字符串都有长度,但是这个长度和数组不一样,不同长度的字符串还是同样的string类型。

我们来看一下字符串的结构,在reflect.StringHeader中,我们可以看到定义:

type StringHeader struct {
    Data uintprt
    Len  int
}

从这里我们可以看到,string其实是一个结构,Data持有的是底层字节数组,Len就是字符串的长度了。 因此对string的复制,也就是StringHeader结构体的复制,并不涉及到底层字节数组的复制,所以没有什么消耗。 另外,相同的字符串面值的常量,对应的一般都是同一个字节数组。 string虽然底层不是切片,但是支持切片的操作。

字符串的使用

遍历字符串

遍历字符串,是使用range来进行的,虽然for也可以遍历字符串的内容,但是会有问题:

func main() {
    s := "123"
    for i := 0; i < len(s); i++ {
        fmt.Println(s[i])
    }
}

output:

49
50
51

等会儿,这个有点和想象的不一样啊,为什么不是123的输出呢? 这是因为Golang是UTF-8编码,123对于的码的值就是这些数字。 我们可以转换成字符再打印。但是还会有其他问题

func main() {
    s := "hi,世界"
    for i := 0; i < len(s); i++ {
        fmt.Printf("%c\n", s[i], s[i])
    }
}

output:

h
i
,
ä
¸

ç


这里又出现乱码了,这个是因为UTF-8在编码的时候,是变长编码的,对于ASCII是采用一个byte来编码的, 而对于其他Unicode字符来说则是两个字节的编码。我们通过for来遍历,其实还是在遍历底层的字节序列。

对于这个,我们当然可以转成Golang中的[]rune类型。(话说用rune,符文,这个词,对于西方世界还是挺形象的)。 []rune其实是[]int32的别名,而不是重新定义的新类型。

func main() {
    s := "hi,世界"
    r := []rune(s)
    for i := 0; i < len(r); i++ {
        fmt.Printf("%c\n", r[i])
    }
}

output:

h
i
,
世
界

这次就对了。

但是一般情况下,我们不需要这么麻烦,是可以用range来遍历的:

func main() {
    s := "hi,世界"
    for _, v := range s {
        fmt.Printf("%c\n", v)
    }
}

output:

h
i
,
世
界

这是因为Golang中的range针对string做了特殊的处理。

截取字符串

截取其中一部分字符串是非常容易的,因为字符串是支持切片操作的。只要substr := str[m:n]即可。

改变中某个字符

因为字符串是不支持直接修改的,所以必须曲线救国。 我们已经知道,字符串是UTF-8编码的了,那么对于只有ASCII的包含非ASCII字符的两种string,可以有不同的改法。

如果字符串只包含ASCII字符,那么可以将string转换成[]byte,然后再来修改。

func main() {
    s := "123"
    bs := []byte(s)
    bs[1] = 'a'
    s = string(bs)
    fmt.Println(s)
    // “1a3”
}

如果字符串中包含Unicode字符,那么需要将string转换成[]rune,然后再进行修改。

func main() {
    s := "hi,世界"
    rs := []rune(s)
    rs[3] = 'w'
    s = string(rs)
    fmt.Println(s)
    // “hi,w界”
}

虽然string支持len操作,但是这个取出来的是底层byte数组的长度。 所以如果字符串全部都是ASCII,可以使用len来获取长度,如果包含unicode,则需要使用utf8.RuneCountInString(str)

func main() {
    s := "123"
    println(len(s))  // 3
    s = "hi,世界"
    println(len(s))  // 9
    println(utf8.RuneCountInString(s)) // 5
}

字符串的拼接

字符串的拼接可以有以下几种方式:

  1. 最简单的,使用+
  2. 最高效的,使用字节拼接
  3. 使用strings包的方法

    func main() {
    s1 := "hi"
    s2 := "世界"
    
    // 1
    println(s1 + s2)
    
    // 2
    var buf bytes.Buffer
    buf.WriteString(s1)
    buf.WriteString(s2)
    println(buf.String())
    
    // 3
    str := strings.Join([]string{s1, s2}, "")
    println(str)
    }
    

字符串与数字的转换

在实际的使用过程中,我们经常会需要将字符串与数字进行转换,可以通过strconv提供的各种函数来实现。

源类型 目标类型 方式 string int string, _ := strconv.Atoi(“1”) int string int := strconv.Itoa(1) string int64 int64, _ := strconv.ParseInt(“1”, 10, 64) int64 string string := strconv.FormatInt(1, 64)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK