golang中你不知道的string

golang中你不知道的 string

字符串对于一篇博客文章来说似乎太简单了,但是一个简单的东西想用好,其实也不容易。

遍历字符串

package main

import (
	"fmt"
	"unicode/utf8"
)

func main() {
	const sample = "我爱golang"
	for i := 0; i < len(sample); i++ {
		runeValue, _ := utf8.DecodeRuneInString(sample[i:])
		fmt.Printf("position:%v, value:%c \n", i, runeValue)
	}
}
复制代码
position:0, value:我 
position:1, value:� 
position:2, value:� 
position:3, value:爱 
position:4, value:� 
position:5, value:� 
position:6, value:g 
position:7, value:o 
position:8, value:l 
position:9, value:a 
position:10, value:n 
position:11, value:g
复制代码

输出的是每一个字节。
如果想输出字符呢?

package main

import (
	"fmt"
	"unicode/utf8"
)

func main() {
	const sample = "我爱golang"
	for i, s := 0, 0; i < len(sample); i = i + s {
		runeValue, size := utf8.DecodeRuneInString(sample[i:])
		fmt.Printf("position:%v, value:%c \n", i, runeValue)
		s = size
	}
}
复制代码

输出

position:0, value:我 
position:3, value:爱 
position:6, value:g 
position:7, value:o 
position:8, value:l 
position:9, value:a 
position:10, value:n 
position:11, value:g 
复制代码

还有更方便的吗?

package main

import (
	"fmt"
)

func main() {
	const sample = "我爱golang"
	for key, v := range sample {
		fmt.Printf("position:%v, value:%c \n", key, v)
	}
}
复制代码

输出

position:0, value:我 
position:3, value:爱 
position:6, value:g 
position:7, value:o 
position:8, value:l 
position:9, value:a 
position:10, value:n 
position:11, value:g 
复制代码
  • golang字符串是由字节构成的,因此索引它们会产生字节,而不是字符
  • 通过unicode、utf-8等编码方式,从[]byte中解码字符串

字符串的长度

package main

import (
	"fmt"
	"unicode/utf8"
)

func main() {
	str := "我爱golang"
	fmt.Println(len(str), "len bytes")
	fmt.Println(utf8.RuneCountInString(str), "len characters") // 更快
	fmt.Println(len([]rune(str)), "len characters")
}
复制代码

输出

12 len bytes
8 len characters
8 len characters
复制代码
  • golang对字符串的底层处理,采用的是[]byte,实际存储的值是一个uint8类型的;
  • 采用UTF-8编码英文字符,每个字符只占用一个byte,而中文需要占用3个byte,因此长度是12;
  • utf8会自动判断每个字符编码占用了几个byte,很清晰的展示了 Golang 对 string 的处理原理;
  • for range string 每次迭代会解码一个 utf-8 编码的字符。

修改字符串

package main

import "fmt"

func main() {
	str := "golang"
	c := []byte(str)
	c[0] = 'c'
	s2 := string(c)
	fmt.Println(s2)
}
复制代码

输出

colang
复制代码

拼接字符串

package main

import (
	"bytes"
	"fmt"
)

func main() {
	str := "我爱"
	str2 := "golang"
	// 该方案每次合并会创建一个新的字符串
	fmt.Println(str + str2)

	// 该方案更更快,直接连接底层的 []byte
	var buffer bytes.Buffer
	buffer.WriteString(str)
	buffer.WriteString(str2)
	fmt.Println(buffer.String())
}

复制代码

WriteString更快的原因,见源码直接底层[]byte连接

func (b *Buffer) WriteString(s string) (n int, err error) {
	b.lastRead = opInvalid
	m, ok := b.tryGrowByReslice(len(s))
	if !ok {
		m = b.grow(len(s))
	}
	return copy(b.buf[m:], s), nil
}
复制代码

参考

更多内容,欢迎关注我的Github