Go语言string底层结构与实战

前言

本文你收货golang中字符串底层结构。了解字符串主要特点,并学习实战如何熟练使用string。

string 是什么

在 go 语言中,字符串实是一个不可改变的字节序列,其数据结构定义如下:

// 定义在 runtime/string.go 文件内
type stringStruct struct {
	str unsafe.Pointer
	len int
}
  • str 是字符串指向底层字节数组的指针
  • len 是字符串的字节长度

底层结构其实是一个结构体,所以字符串的赋值操作,也就是结构的复制过程,并不包含指针指向的内容的复制。下图是字符串hello存储结构示意图:

Go语言string底层结构与实战_第1张图片

string 特点

  • 字符串是一个不可改变的字节序列,不可修改
  • 字符串支持切片操作,不同位置的切片底层访问的是同一块内存数据
  • 由于只读的特性,相同字符串面值常量通常对应同一个字符串常量

如何使用string

字符串声明与输出

代码示例

package main

import "fmt"

func main() {

	// 定义
	s := "hello " 
	var a string // 空字符串
	b := string("world")
	fmt.Println(s)
	fmt.Println(a)
	fmt.Println(b)
}

程序输出

"hello"

"world"

字符串比较与长度

这里需要注意的是,字符串并不支持cap(),会编译报错,原因是底层结构只有Len字段表示其长度。

代码示例

import "fmt"

func main() {

	s1 := "hello"
	s2 := "world"
	// 判断是否相等
	fmt.Println(s1 == s2)
	// 判断是否不等
	fmt.Println(s1 != s2)
	// 获取长度
	fmt.Println(len(s1))
	
	// 字符串没有容量的概念记住!
	// 下面这行会编译报错: s1 (variable of type string) for cap
	// fmt.Println(cap(s1))
}

代码输出

false
true
5

字符串遍历

代码示例

例子1:遍历普通字符串,输出字符与ascii数值

package main

import "fmt"

func main() {
	s := "hello"
	// 遍历输出ASCII值
	for i, c := range s {
		fmt.Printf("%d-%d-%c \n", i, c, c)
	}

}

代码输出

0-104-h 
1-101-e 
2-108-l 
3-108-l 
4-111-o 

例子2:有汉字情况下输出的是utf8值,注意观察第一列的下标变化

package main
import "fmt"
func main() {
	// 如果有汉字
	s := "你好, world"
	for i, c := range s {
		fmt.Printf("%d-%d-%c \n", i, c, c)
	}
}


输出

0-20320-你 
3-22909-好 
6-44-, 
7-32-  
8-119-w 
9-111-o 
10-114-r 
11-108-l 
12-100-d 

例子3:看一下直接用下标遍历会出现什么情况?

package main
import "fmt"
func main() {
	// 如果有汉字
	s := "你好, world"
	for i := 0; i < len(s); i++ {
		fmt.Printf("%d-%d-%c \n", i, s[i], s[i])
	}
}

输出:竟然乱码了,猜一下为什么?

0-228-ä 
1-189-½ 
2-160-  
3-229-å 
4-165-¥ 
5-189-½ 
6-44-, 
7-32-  
8-119-w 
9-111-o 
10-114-r 
11-108-l 
12-100-d 

原因:用下标索引字符串可以访问单个字节,而不是字符,range遍历的是字符

字符串拼接的两种方式

  • 用+直接连接两个字符串,但是无法直接连接数字,需要先把数子转为字符串类型
  • 直接用Sprintf函数格式化,可以直接连接数字
package main

import "fmt"

func main() {
	//直接用+,无法与数字连接
	s := "hello"
	s += "world"
	fmt.Println(s)
	//格式化拼接,可以连接数字
	s1 := fmt.Sprintf("%s-%d", "hello", 888)
	fmt.Println(s1)
}

输出

helloworld
hello-888

总结

  1. 在 go 语言中,字符串实是一个不可改变的字节序列,包含一个指针与长度字段
    字符串是一个不可改变的字节序列

  2. 字符串并不支持cap(),会编译报错,原因是底层结构只有Len字段

  3. 字符串range 遍历输出的是rune(int32)值,可能是utf8值或者ASCII,用下标遍历输出的是字符值

你可能感兴趣的:(go语言实战,golang,go,开发语言,后端,经验分享)