文章目录
Golang 标准库(一)
fmt
提供格式化输入与输出操作。
格式化输出
打印到控制台:
- func Print(a …interface{}) (n int, err error)
- 打印任何数据。
- func Println(a …interface{}) (n int, err error)
- 打印任何数据并且换行。
- func Printf(format string, a …interface{}) (n int, err error)
- 格式化打印任何数据。
- %v 任意值
- %+v 任意值,如果是struct则带字段
- %#v 任意值,用 Go 的语法表示
- %T 打印值的类型
- %t 布尔值
- %s 字符串
- %d 整数,十进制
- %x 整数,十六进制,使用a-f
- %X 整数,十六进制,使用A-F
- %o 整数,八进制
- %b 整数,二进制
- %f 浮点数,默认宽度,默认精度
- %.2f 浮点数,默认宽度,精度 2,四舍五入
- %9.2f 浮点数,宽度9(不够补空格),精度 2
生成字符串:
- func Sprint(a …interface{}) string
- func Sprintln(a …interface{}) string
- func Sprintf(format string, a …interface{}) string
输出到 io:
- func Fprint(w io.Writer, a …interface{}) (n int, err error)
- func Fprintf(w io.Writer, format string, a …interface{}) (n int, err error)
- func Fprintln(w io.Writer, a …interface{}) (n int, err error)
生成 error:
- func Errorf(format string, a …interface{}) error
x := struct {
name string
age int
}{"john", 10}
fmt.Printf("%vn", x) //{john 10}
fmt.Printf("%+vn", x) //{name:john age:10}
fmt.Printf("%#vn", x) //struct { name string; age int }{name:"john", age:10}
fmt.Printf("%9.2fn", 123.456) // 123.46,前面有3个空格整体宽度9字符
格式化输入
控制台输入:
- func Scan(a …interface{}) (n int, err error)
- 用空白符(空格、制表、换行)作为分隔符依次输入到参数,传指针。
- 空白符可以多个。
- 输入完成或出错才返回,返回成功个数和失败原因。
- func Scanln(a …interface{}) (n int, err error)
- 用空白符(空格、制表)作为分隔符依次输入到参数,遇到换行结束输入。
- func Scanf(format string, a …interface{}) (n int, err error)
- 格式化输入,必须按照 format 要求的格式输入,否则报错。
字符串输入:
- func Sscan(str string, a …interface{}) (n int, err error)
- func Sscanln(str string, a …interface{}) (n int, err error)
- func Sscanf(str string, format string, a …interface{}) (n int, err error)
io 输入:
- func Fscan(r io.Reader, a …interface{}) (n int, err error)
- func Fscanln(r io.Reader, a …interface{}) (n int, err error)
- func Fscanf(r io.Reader, format string, a …interface{}) (n int, err error)
strings
提供字符串相关操作,包括大小写转换、修剪、查找替换、拆分拼接等。
字符串处理函数
- 大小写
- func EqualFold(s, t string) bool
- 不区分大小写比较字符串
- 如果是区分大小写,使用运算符
- func ToLower(s string) string
- func ToUpper(s string) string
- 修剪
- func Trim(s, cutset string) string
- func TrimLeft(s, cutset string) string
- func TrimRight(s, cutset string) string
- func TrimSpace(s string) string
- func TrimPrefix(s, prefix string) string
- func TrimSuffix(s, suffix string) string
- func TrimFunc(s string, f func(rune) bool) string
- 查找
- func Contains(s, substr string) bool
- func HasPrefix(s, prefix string) bool
- func HasSuffix(s, suffix string) bool
- func Index(s, substr string) int
- 替换
- func Replace(s, old, new string, n int) string
- func ReplaceAll(s, old, new string) string
- 拆分与拼接
- func Split(s, sep string) []string
- func Join(elems []string, sep string) string
strings.Builder
- 用于高效处理字符串拼接,如果是少量拼接用运算符+。
- func (b *Builder) WriteString(s string) (int, error)
- 写入字符串,返回长度,error 为 nil
- func (b *Builder) String() string
- 返回字符串
var sb strings.Builder
sb.WriteString("Hello")
sb.WriteString("World")
fmt.Println(sb.String()) //HelloWorld
strconv
提供字符串转换相关操作。
s := strconv.Itoa(123) //int 转字符串,十进制
fmt.Println(s)
i, err := strconv.Atoi("123") //字符串转 int,十进制
fmt.Println(i, err)
i64, err := strconv.ParseInt("666", 8, 64) //字符串转 int,8 进制
strBit := strconv.FormatInt(i64, 2) //int 转字符串,2 进制
fmt.Println(strBit, err) //八进制 666 对应二进制 110110110
b, err := strconv.ParseBool("TRUE") //1 t T TRUE true True都为真,0 f F FALSE false False为假
fmt.Println(b, err) //true
f, err := strconv.ParseFloat("123.4567", 64) //字符串转 float64
fmt.Println(f, err)
time
提供时间相关操作,包括取系统时间与时区、格式化转换、比较、定时器、协程睡眠等。
//获取当前时间
now := time.Now()
fmt.Println("1: ", now)
//转字符串 24小时制
strNow := now.Format("2006-01-02 15:04:05")
fmt.Println("2: ", strNow)
fmt.Println("3: ", now.Format("2006-01-02 03:04:05 PM")) //12小时制
//字符串转日期,使用 0 时区
utcTime, err := time.Parse("2006-01-02 15:04:05", "2023-01-02 14:00:00")
fmt.Println("4: ", utcTime, err)
//字符串转日期,使用系统的本地时区
localTime, err := time.ParseInLocation("2006-01-02 15:04:05", "2023-01-02 14:00:00", time.Local)
fmt.Println("5: ", localTime, err)
//获取年月日时分秒
fmt.Println("6: ", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())
//获取unix时间戳(UTC1970起秒数)
fmt.Println("7: ", now.Unix())
//比较是否相等,会考虑时区影响
fmt.Println("8: ", now.Equal(now.UTC()))
//日期加减
fmt.Println("9: ", now.AddDate(-1, 2, 3)) //年-1,月+2,日+3
//时间加减
fmt.Println("10:", now.Add(time.Hour*2-time.Minute*2)) //时+2,分-2
//比较时间大小
fmt.Println("11:", now.Before(now.AddDate(0, 0, 1)))
fmt.Println("12:", now.After(now.AddDate(0, 0, -1)))
//时间差,获取时间间隔
duration := now.Sub(now.AddDate(0, 0, -1))
fmt.Println("13:", duration, duration.Hours(), duration.Minutes(), duration.Seconds())
//time.Since() 等于 time.Now().Sub()
fmt.Println("14:", time.Since(now.AddDate(0, 0, -2)))
//定时器,执行一次
time.AfterFunc(2*time.Second, func() {
fmt.Println("15: ", time.Since(now))
})
//定时器,间隔触发
ticker := time.NewTicker(3 * time.Second)
go func() {
for x := range ticker.C {
fmt.Println("16: ", x.Sub(now))
}
}()
//协程睡眠
time.Sleep(time.Second * 10)
ticker.Stop()
/*输出结果
1: 2023-09-14 13:20:17.650942692 +0800 CST m=+0.000047084
2: 2023-09-14 13:20:17
3: 2023-09-14 01:20:17 PM
4: 2023-01-02 14:00:00 +0000 UTC <nil>
5: 2023-01-02 14:00:00 +0800 CST <nil>
6: 2023 September 14 13 20 17
7: 1694668817
8: true
9: 2022-11-17 13:20:17.650942692 +0800 CST
10: 2023-09-14 15:18:17.650942692 +0800 CST m=+7080.000047084
11: true
12: true
13: 24h0m0s 24 1440 86400
14: 48h0m1.640158075s
15: 3.640260837s
16: 4.640267816s
16: 7.642234665s
16: 10.642233452s
*/
math
提供数学相关操作,包括各种数字类型的最大值常量、取整、取随机数、数学函数等。
- 常量 MaxInt/MaxInt8/MaxUint/MaxFloat32/MaxFloat64/Pi等
- 向上取整 func Ceil(x float64) float64
- 向下取整 func Floor(x float64) float64
- 四舍五入取整 func Round(x float64) float64
- 取绝对值 func Abs(x float64) float64
- 取最大 func Max(x, y float64) float64
- 取最小 func Min(x, y float64) float64
- 取随机整数 func Intn(n int) int
- 取随机浮点数,范围[0, 1.0) Float64() 和 Float32()
- 其他常用函数,如三角函数、对数指数等
sort
提供排序相关操作,支持基本类型或自定义类型的排序。
内部实现了插入排序、归并排序、堆排序和快速排序,会根据数据量和是否稳定排序自动选择算法,确保效率。
基本类型排序
标准库定义了以下类型,并实现了排序接口:
- type IntSlice []int
- type Float64Slice []float64
- type StringSlice []string
//int切片正序
arrInt := []int{5, 3, 7, 1, 9}
sort.Ints(arrInt)
fmt.Println(arrInt)//[1 3 5 7 9]
//int切片逆序
sort.Sort(sort.Reverse(sort.IntSlice(arrInt)))
fmt.Println(arrInt)//[9 7 5 3 1]
//float切片正序
arrFloat := []float64{2.0, 9.3, 2.3, 1.1, 6.3}
sort.Float64s(arrFloat)
fmt.Println(arrFloat)//[1.1 2 2.3 6.3 9.3]
//float切片逆序
sort.Sort(sort.Reverse(sort.Float64Slice(arrFloat)))
fmt.Println(arrFloat)//9.3 6.3 2.3 2 1.1]
//string切片正序
arrStr := []string{"x3", "x1", "v", "z", "b", "a"}
sort.Strings(arrStr)
fmt.Println(arrStr)//[a b v x1 x3 z]
//string切片逆序
sort.Sort(sort.Reverse(sort.StringSlice(arrStr)))
fmt.Println(arrStr)//[z x3 x1 v b a]
自定义类型排序
type Person struct {
Name string
Age int
}
type PersonSlice []Person
func (s PersonSlice) Len() int { return len(s) }
func (s PersonSlice) Less(i, j int) bool { return s[i].Age < s[j].Age }
func (s PersonSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func main() {
persons := PersonSlice{{"a", 20}, {"b", 5}, {"c", 10}, {"d", 5}}
//对实现了 sort.Interface 接口的类型排序
sort.Sort(persons)
//稳定排序
sort.Stable(persons)
fmt.Println(persons) //[{b 5} {d 5} {c 10} {a 20}]
//二分查找,返回最小符合条件的元素索引,注意后面元素必须全部符合条件
x := sort.Search(len(persons), func(i int) bool {
return persons[i].Age >= 10
})
fmt.Println(persons[x:]) //[{c 10} {a 20}]
//直接传入 Less 函数排序,无需实现 sort.Interface 接口
sort.Slice(persons, func(i, j int) bool {
return persons[i].Name < persons[j].Name
})
fmt.Println(persons) //[{a 20} {b 5} {c 10} {d 5}]
}
container
提供容器数据类型,包括堆、链表和环。
heap
- container/heap包提供对任意类型(实现了heap.Interface接口)的堆操作。
- 小根堆:父节点小于或等于子节点,堆顶最小。
- 使用场景:优先队列,Top N 问题。
- 使用方法:
- 实现heap.Interface接口。
- 调用heap.Init()初始化堆。
- 调用heap.Push()添加元素。
- 调用heap.Pop()取堆顶。
- 调用heap.Remove()移除某个元素。
- 调用heap.Fix()修复堆。
//container/heap包中的heap.Interface定义
type Interface interface {
sort.Interface
Push(x any)
Pop() any
}
//示例:通过堆实现矩形按面积从小到大的优先队列
type Rectangle struct {
Width int
Height int
}
func (r *Rectangle) Area() int {
return r.Width * r.Height
}
type RectHeap []Rectangle
func (h RectHeap) Len() int { return len(h) }
func (h RectHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h RectHeap) Less(i, j int) bool { return h[i].Area() < h[j].Area() }
func (h *RectHeap) Push(x interface{}) { *h = append(*h, x.(Rectangle)) }
func (h *RectHeap) Pop() interface{} {
n := len(*h)
x := (*h)[n-1]
*h = (*h)[:n-1]
return x
}
func main() {
h := &RectHeap{}
heap.Init(h)
for i := 0; i < 10; i++ {
heap.Push(h, Rectangle{Width: 1 + rand.Intn(10), Height: 1 + rand.Intn(10)})
}
for h.Len() > 0 {
fmt.Println(heap.Pop(h))
}
}
list
- container/list包提供双向链表。
- Element 元素类型
- Next()后继结点
- Prev()前驱结点
- List 链表
- New() 创建链表
- Front() 链表头部
- Back() 链表尾部
- Len() 链表长度
- PushBack() 链表尾部添加元素
- PushFront() 链表头部添加元素
- Remove() 删除元素
- MoveToBack() 移动到尾部
- MoveToFont() 移动到头部
l := list.New()
for i := 0; i < 10; i++ {
l.PushBack(i)
}
for e := l.Front(); e != nil; e = e.Next() {
println(e.Value.(int))
}
ring
- container/ring包提供环数据结构。
- Ring 既是元素又是环
- New(n) 创建环。
- Next() 下一个元素。
- Prev() 上一个元素。
- Len() 环的长度。
- Do(func) 循环执行操作。
r := ring.New(3)
for i := 1; i <= 3; i++ {
r.Value = i
r = r.Next()
}
r.Do(func(p interface{}) {
println(p.(int))
})
os
提供操作系统相关操作,包括文件、目录、进程、环境变量等操作。
//写入文件,0666为Unix权限代码,八进制0666等于二进制110110110,表示三种身份都是读写
if err := os.WriteFile("a.txt", []byte("hello world"), 0b110110110); err != nil {
fmt.Println(err)
}
//读取文件
buf, err := os.ReadFile("a.txt")
if err != nil {
fmt.Println(err)
}
fmt.Println(string(buf))
//删除文件
if err := os.Remove("a.txt"); err != nil {
fmt.Println(err)
}
//创建多级目录
if err := os.MkdirAll("b/c", 0666); err != nil {
fmt.Println(err)
}
//删除目录
if err := os.RemoveAll("b"); err != nil {
fmt.Println(err)
}
//获取临时目录
tmpDir := os.TempDir()
fmt.Println(tmpDir)
//获取工作目录
dir, err := os.Getwd()
if err != nil {
fmt.Println(err)
} else {
fmt.Println(dir)
}
//获取环境变量
x := os.Getenv("GOPATH")
fmt.Println(x)
//进程退出,0 为正常,非 0 为出错
os.Exit(0)
sync
提供处理同步的工具,包括互斥锁、读写锁等。
WaitGroup
组等待,用于实现主协程等待指定数量的子协程执行完成后再继续执行。
func main() {
wg := sync.WaitGroup{}
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
fmt.Print(i, " ")
}(i)
}
wg.Wait()
}
互斥锁
- 互斥锁(sync.Mutex)用于保证在任意时刻,只有一个协程访问某个数据。
- 满足以下条件,协程获取不到锁将进入自旋状态,否则进入等待队列:
- 还没自旋超过4次
- 多核CPU并且P数量大于1
- P上本地队列为空
- 自旋的协程比刚从等待队列中唤醒的协程更容易获取到锁,可能导致饥饿现象。
- 互斥锁的饥饿模式
- 一个G等待锁超过1毫秒,锁进入饥饿模式。
- 饥饿模式下,新获取锁的协程禁用自旋,而是加入等待队列尾部。
type Counter struct { //支持加锁的计数器
sync.Mutex//嵌套,使Counter支持Mutex的方法
Data int
}
func main() {
counter := Counter{}
wg := sync.WaitGroup{} //用于辅助主协程等待
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
counter.Lock() //加锁确保不会并发修改
defer counter.Unlock() //延迟解锁
counter.Data++
}()
}
wg.Wait()
fmt.Println(counter.Data)
//如果没有发生并发修改,将输出1000,否则小于1000
}
读写锁
读写锁(sync.RWMutex)可以被同时多个读取者持有或唯一个写入者持有。
func main() {
m := sync.RWMutex{}
wg := sync.WaitGroup{}
start := time.Now()
for i := 0; i < 3; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
m.Lock()
fmt.Println(int(time.Since(start).Seconds()), "write begin ", i)
defer m.Unlock()
time.Sleep(time.Second * 5)
fmt.Println(int(time.Since(start).Seconds()), "write end ", i)
}(i)
}
time.Sleep(time.Second * 1)
for i := 0; i < 3; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
m.RLock()
fmt.Println(int(time.Since(start).Seconds()), "read begin ", i)
defer m.RUnlock()
time.Sleep(time.Second * 1)
fmt.Println(int(time.Since(start).Seconds()), "read end ", i)
}(i)
}
wg.Wait()
}
/*输出结果可见:写独占读并发
0 write begin 2
5 write end 2
5 read begin 2
5 read begin 0
5 read begin 1
6 read end 1
6 read end 2
6 read end 0
6 write begin 0
11 write end 0
11 write begin 1
16 write end 1
*/
sync.Map
Go 的内建类型map支持并发读,但不支持并发写。有两种方法来实现 map 并发读写:
- 用 map 结合 RWMutex
- go 1.9 之后用标准库 sync.Map
- 区别是后者以空间换时间,内部采用读写分离两个 map,读时不需要加锁。
m := sync.Map{}
for i, c := range "abcdefg" {
m.Store(string(c), i) //写入
}
v1, ok := m.Load("c") //读取
fmt.Println(v1, ok) //2 true
m.Delete("c") //删除
v2, ok := m.Load("c") //再次读取,不存在
fmt.Println(v2, ok) //<nil> false
//遍历
m.Range(func(key, value interface{}) bool {
fmt.Println(key, value)
return true //返回 false 终止循环
})
原子操作
- 原子操作是不会被线程调度机制打断的操作。
- 原子操作是线程安全的,可以在并发下读写而无需加锁。
- 原子操作通过底层系统调用来实现。
- 通过sync/atomic包提供原子操作函数。
- Add系列函数用于给变量增加值并返回结果。
- CompareAndSwap系列函数用于比较并交换变量的值。
- Swap系列函数用于将变量设置为给定值并返回旧值。
- Load系列函数用于原子读取变量的值。
- Store系列函数用于原子写入变量的值。
var num int32 = 0
var wg sync.WaitGroup
wg.Add(10)
for i := 0; i < 10; i++ {
go func() {
for j := 0; j < 10000; j++ {
atomic.AddInt32(&num, 1)
// num++ //多核同时运行存在问题
}
wg.Done()
}()
}
wg.Wait()
fmt.Println(atomic.LoadInt32(&num)) //100000