1、Go的String其实是类切片的数据结构
package main
import (
"fmt"
String "modLearn/String/demo"
)
func main() {
s := "hello world"
for i := 0; i < len(s); i++ {
u := s[i]
// 68 65 6c 6c 6f 20 77 6f 72 6c 64
fmt.Printf("%x ", u)
}
}
直接使用 for
循环来打印字符串,并没有达到想要的效果,将每个字符打印出来,而是以十六进制打印出了它的byte值。这是因为Go的字符串其实就是uint8数组,如下图:
这也是为什么使用打印出来时是数字,如果想打印出对应的字符,有两种做法
- 1、在打印时使用强制类型转换
fmt.Printf("%s", string(u))
- 2、使用
strings.Split(s,"")
切割字符串,切割后会变成字符串数组,再用for循环打印就可以打印出想要的字符了
2、string类型的值是常量,不可更改
尝试使用索引遍历字符串,来更新字符串中的个别字符串,是不允许的。string类型的值是只读的二进制byte array,如果真想修改字符串中的字符,将string转为[]byte修改后,再转为string即可
package main
import "fmt"
func main() {
// 错误示范
x := "text"
//x[0] = "T" // error: cannot assign to x[0]
//fmt.Println(x)
xBytes := []byte(x)
xBytes[0] = 'T' // 注意此时的 T 是 rune 类型
x = string(xBytes)
fmt.Println(x) // Text
// 推荐做法
xRunes := []rune(x)
xRunes[0] = '我'
x = string(xRunes)
fmt.Println(x) // 我ext
}
使用[]byte()并不是更新字符串的正确姿势,因为一个UTF8编码的字符可能会占多个字节,比如汉字就需要3-4个字节来存储,此时更新其中的一个字节是错误的。
更新字符串的正确姿势:将string转为rune slice(此时1个rune可能占多个byte),直接更新rune中字符。
3、字符串的长度
func main() {
char := "♥"
fmt.Println(len(char)) // 3
Go的内建函数len()返回的是字符串的byte数量,而不是像python中那样计算Unicode字符数。
如果要得到字符串的字符数,可使用"unicode/utf8"包中的 RuneCountInString(str string) (n int))
注意:
RuneCountInString
并不总是返回我们看到的字符数,因为有的字符会占用 2 个 rune:
char := "♥"
fmt.Println(len(char)) // 3
fmt.Println(utf8.RuneCountInString(char)) // 1
char1 := "é"
fmt.Println(len(char1)) // 3
fmt.Println(utf8.RuneCountInString(char1)) // 2
fmt.Println("cafe\u0301") // café // 法文的 cafe,实际上是两个 rune 的组合
运行结果:
4、Array是值类型,不是引用类型,但是切片是引用类型
Go中Array当成参数传给函数时,是按值传递的,也就是一个完全值拷贝,在函数里修改参数数组的值,并不会影响原始值,但是切片会影响,代码如下:
package main
import (
"fmt"
)
func main() {
// Array是值类型,而不是引用类型,函数传参时,在函数里修改数组,并不会更新数组的值
// Slice是引用类型,在函数里修改,会影响原来的值
arr := [3]int64{1, 2, 3}
ChangeArrItem(arr)
fmt.Println(arr) // [1,2,3]
s := []int64{1, 2, 3}
ChangeSliceItem(s) // [1,2,3]
fmt.Println(s)
}
func ChangeArrItem(arr [3]int64) {
arr[0] = 100
}
func ChangeSliceItem(arr []int64) {
arr[0] = 100
}
运行结果:
5、defer函数的参数值是声明时求值的
对defer延迟执行的函数,他的参数会在声明的时候就算出具体值,而不是在执行时才求值
package main
import "fmt"
func main() {
// defer函数的参数值,对 defer 延迟执行的函数,它的参数会在声明时候就会求出具体值,而不是在执行时才求值
var i = 1
// result: 3, 而不是6
defer fmt.Println("result1: ", func() int { return i * 3 }())
i++
}
但是可以通过闭包传值来实现想要的结果
defer func(i int){
fmt.Println("result2: ", func() int { return i * 3 }())
}(i)
运行结果:
6、defer执行顺序是栈形式的,后进先出,后进的defer函数先执行
就用上面的图:可以看到后声明的defer会先执行,先打印了result2: 6
,后打印result1: 3
7、程序退出时还有goroutine在执行
主程序默认不等所有goroutine都执行完才退出,这点需要特别注意
package main
import (
"fmt"
"time"
)
func main() {
workCount := 2
for i := 0; i < workCount; i++ {
go doIt(i)
}
time.Sleep(1 * time.Second)
fmt.Println("all done!")
}
func doIt(workId int) {
fmt.Printf("[%v] is running \n", workId)
time.Sleep(3 * time.Second) // 模拟goroutine正在执行
fmt.Printf("[%v] is done \n", workId)
}
如图,main()主程序不等两个goroutine执行完就直接退出了。常见的解决办法:使用WaitGroup变量
,它会让主程序等待所有goroutine执行完毕再退出。
如果你的goroutine要做消息的循环处理耗时操作,可以 向它们发送一条kill
消息来关闭它们。或直接关闭一个它们都等待接受数据的channel:
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
done := make(chan struct{})
ch := make(chan interface{})
workerCount := 2
for i := 0; i < workerCount; i++ {
wg.Add(1)
go doIt(i, ch, done, &wg) // wg 传指针,doIt() 内部会改变 wg 的值
}
for i := 0; i < workerCount; i++ { // 向 ch 中发送数据,关闭 goroutine
ch <- i
}
close(done)
wg.Wait()
close(ch)
fmt.Println("all done!")
}
func doIt(workId int, ch <-chan interface{}, done <-chan struct{}, wg *sync.WaitGroup) {
fmt.Printf("[%v] is running \n", workId)
defer wg.Done()
for {
select {
case m := <-ch:
fmt.Printf("[%v] m => %v\n", workId, m)
case <-done:
fmt.Printf("[%v] is done\n", workId)
return
}
}
}
运行结果:
8、向无缓冲的channel发送数据,只要receiver准备好了就会立刻返回
只有在数据被receiver处理时,sender才会被阻塞。因运行环境差异,在sender发送完数据后,recevier的goroutine可能没有足够的时间处理下一个数据
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan string)
go func() {
for m := range ch {
time.Sleep(time.Second)
fmt.Println("我收到了", time.Now().Format("15:04:05"))
fmt.Println("Processed:", m)
time.Sleep(10 * time.Second) // 模拟需要长时间运行的操作
fmt.Println("我处理完了", time.Now().Format("15:04:05"))
}
}()
fmt.Println("开始发送第一条数据", time.Now().Format("15:04:05"))
ch <- "cmd.1"
fmt.Println("第一条数据处理结束", time.Now().Format("15:04:05"))
ch <- "cmd.2" // 不会被接收处理
}
运行结果: