A string is a slice of bytes in Go.
Strings in Go are Unicode compliant and are UTF-8 Encoded.
即在golang中,字符串使用uft-8进行编码,底层是字节数组([]byte).

字符串声明

func main() {
	str1 := "单行字符串"
	fmt.Println(str1)
	str2 := `多行字符串
hello world
	`
	fmt.Println(str2)
}

访问字符串底层字节

func printBytes(s string) {
	fmt.Printf("Bytes: ")
	for i := 0; i < len(s); i++ {
    // %v 输出字节本身的值,%x 以16进制输出
		fmt.Printf("%x(%v) ", s[i], s[i])
	}
  // 输出: 
  //Bytes: e4(228) bd(189) a0(160) e5(229) a5(165) bd(189)
}

func main() {
	name := "你好"
	fmt.Printf("String: %s\n", name)
	printBytes(name)
}

从上面的输出可知,golang中,一个中文字符占3个字节。

访问字符串的单个字符

例如,依次输出hello 字符串的每个字符。

func printChars(s string) {
	fmt.Printf("Characters: ")
	for i := 0; i < len(s); i++ {
    // %c the character represented by the corresponding Unicode code point
    // 即输出Unicode形式的字符
		fmt.Printf("%c ", s[i])
	}
 //输出:Characters: h e l l o

}

func main() {
	name := "hello"
	fmt.Printf("String: %s\n", name)
	printChars(name)
}

上述代码看似没有问题,因为 len(s) 返回字符串的字节数,而对于英文字符串一个字符正好占用一个字节,因此使用 for 循环遍历没有问题。
但是,如果是中文字符串,一个字符占用三个字节,使用上述方式就有问题了。

func printChars(s string) {
	fmt.Printf("Characters: ")
	for i := 0; i < len(s); i++ {
		fmt.Printf("%c ", s[i])
	}
  // 输出:Characters: ä ½   å ¥ ½
}

func main() {
	name := "你好"
	fmt.Printf("String: %s\n", name)
	printChars(name)
}

使用Rune访问字符串单个字符

rune 是golang中的内置类型,也是int32的别名类型,专用于存储Unicode编码的单个字符,而不管这个字符占用多少个字节。
下面的示例,我们将字符串转换为 rune切片再进行输入,对别如下:

func printCharsWithRune(s string) {
	fmt.Printf("Characters: ")
	runestr := []rune(s)
	for i := 0; i < len(runestr); i++ {
		fmt.Printf("%c ", runestr[i])
	}
	fmt.Println()
}

func main() {
	name1 := "你好"
	name2 := "hello"
	printCharsWithRune(name1)
	printCharsWithRune(name2)
  // 分别输出:
  // Characters: 你 好
  // Characters: h e l l o
}

使用for range loop 访问字符串字符

func printCharsWithRuneAndForRange(s string) {
	for index, runes := range s {
		fmt.Printf("%c starts at byte %d\n", runes, index)
	}
}

func main() {
	name := "你好"
	printCharsWithRuneAndForRange(name)
}
//输出
//你 starts at byte 0
//好 starts at byte 3

通过切片创建字符串

func main() {
	str := "你好"
	bytes := []byte(str)
	fmt.Println(bytes)
	// byteSlice := []byte(bytes)
	byteSlice := []byte{228, 189, 160, 229, 165, 189}
	str1 := string(byteSlice)
	fmt.Println(str1)
}
// 输出
//[228 189 160 229 165 189]
//你好

当然,除了十进制,也可以使用16进制的字节切片

func main() {
	byteSlice := []byte{0xe4, 0xbd, 0xa0, 0xe5, 0xa5, 0xbd}
	str1 := string(byteSlice)
	fmt.Println(str1)
}
// 输出: 你好

当然,如果接受的是Unicode,也可以通过 []rune切片创建字符串。

func main() {

	str := "你好"
	textQuoted := strconv.QuoteToASCII(str)
	// 获取字符串的 Unicode code
	textUnquoted := textQuoted[1 : len(textQuoted)-1]
	fmt.Println(textUnquoted) // 输出\u4f60\u597d
	runeSlice := []rune{0x4F60, 0x597D}
	str1 := string(runeSlice)
	fmt.Println(str1) // 输出:你好

}

获取字符串的长度

我们知道 len(s) 可以获取字符串的字节数,那如果要获取字符串的字符数呢? 可以使用 RuneCountInString(s string) (n int)

func main() {
	str := "你好"
	fmt.Printf("字符数:%d\n", utf8.RuneCountInString(str))
	fmt.Printf("字节数:%d", len(str))
}
//输出:
//字符数:2
//字节数:6

字符串的比较和拼接

在golang 中,比较字符串相等,可以使用 == 操作符合。

而,拼接字符串有两种方式:

  • 使用 +操作符。
  • 使用 Sprintf 格式化输出
s1 := "你"
s2 := "好"
fmt.Println(s1 + s2)
fmt.Println(fmt.Sprintf("%s%s", s1, s2))

字符串不可变性

在golang中,字符串时不可变的。

func main() {
	// =================== 字符串可不改变性 ===============
	// s1 := "你好"
	// s1[0] = '他' // 不可直接修改单个rune字符

	s1 := "你好"
	runes := []rune(s1)
	runes[0] = '他' // 注意是单引号
	fmt.Println(string(runes)) // 他好
	fmt.Println(s1) // 你好 

}