go中一些常用的基础方法
字符串
1. 使用正则表达式验证字符串
使用regexp包中的Match函数:
func Match(pattern string, b []byte) (matched bool, err error)
Match函数返回一个表示在b中是否包含和pattern匹配的项的布尔值和一个错误。
package main
import (
"fmt"
"regexp"
)
func main() {
// 正则表达式模式"^[\u4e00-\u9fa5]+$"表示从头到尾包含的内容是一个或多个中文字符串
// 从\u4e00到\u9fa5是中文的Unicode编码,使用双引号,对应的编码会被解析为相应的中文
matched, err := regexp.Match("^[\u4e00-\u9fa5]+$", []byte(`匹配中文字符`))
// 不能使用反引号,因为反引号引用的是原生的字符,而正则表达式本身没有\u这种写法(有\s等写法,详情可以专门看正则表达式了解)
// matched, err = regexp.Match(`^[\u4e00-\u9fa5]+$`, []byte(`匹配中文字符`))
if err != nil {
fmt.Println("正则表达式匹配出错1", err)
} else {
fmt.Println("正则表达式匹配的结果1:", matched)
}
// 正则表达式模式匹配英文大小写字母和下划线
matched, err = regexp.Match(`^[a-zA-Z_]+$`, []byte(`AAZ123`))
if err != nil {
fmt.Println("正则表达式匹配出错2", err)
} else {
fmt.Println("正则表达式匹配的结果2:", matched)
}
matched, err = regexp.Match(`^[a-zA-Z_]+$`, []byte(`abc_DEF`))
if err != nil {
fmt.Println("正则表达式匹配出错3", err)
} else {
fmt.Println("正则表达式匹配的结果3:", matched)
}
}
在go中,单引号 '' 用来引用单个字符,双引号 "" 用来引用可解析的字符,反引号 `` 用来引用原生的字符,比如"\n"会被解析为一个换行符,但是反引号引用的\n就是一个反斜杠和字符n。
2. 字符串和数字的相互转换
使用strconv包中的函数。
(1)string
类型int
类型的相互转换
package main
import (
"fmt"
"strconv"
)
func main() {
// 字符串转整型
aStr := "123"
if i, err := strconv.Atoi(aStr); err == nil {
fmt.Println("字符串转换为整型后的值", i)
}
// 整型转字符串
aInt := -123
anotherStr := strconv.Itoa(aInt)
fmt.Println("整型转字符串之后的值", anotherStr)
}
Atoi
(ASCII string to integer)等同于ParseInt(s, 10, 0)
,将字符串转换为基数为10进制的int类型。
func ParseInt(s string, base int, bitSize int) (i int64, err error)
将一个字符串解析为整数,base是给定的基数(0、2到36),bitSize是位的大小(0到64),返回的值是和bitSize以及符号匹配的最大的整数。
当基数(base参数)为0的时候,函数会根据字符串的前缀来判断使用的是几进制,"0b"前缀对应2进制,"0" 或者"0o"前缀对应8进制, "0x"前缀对应16 进制,其他前缀对应10进制。
位的大小(bitSize参数)0,8,16,32,和 64 对应 int,int8,nt16,int32,和int64。
package main
import (
"fmt"
"unsafe"
)
func main() {
var a int = 1
fmt.Println("int类型的大小", unsafe.Sizeof(a)) // 打印的内容为 int类型的大小 8
}
int类型具体的大小和操作系统有关,对于32位的操作系统,int类型就是4字节(32位),对于64位的操作系统,int类型的大小就是8字节(64位)。
Itoa
等同于FormatInt(int64(i), 10)
,将整型按照10进制转换为字符串。
func FormatInt(i int64, base int) string
基数大于等于2小于等于23,小写的字母'a'
到'z'
表示大于等于10的位的值(比如对于16进制来说,'a'
表示10,'f'
表示15)。
package main
import (
"fmt"
"strconv"
)
func main() {
fmt.Println(strconv.FormatInt(23456, 16)) // 打印内容:5ba0
}
(2)string
类型和bool
类型,float
类型,uint
类型的相互转换
浮点数转字符串:
func FormatFloat(f float64, fmt byte, prec, bitSize int) string
fmt是格式(format),有以下这些格式:
'b'
表示二进制指数,-ddddp±ddd
'e'
表示十进制指数,-d.dddde±dd
'E'
表示十进制指数,-d.ddddE±dd
;(大写的'E'
和小写的'e'
都表示的是是十进制指数)'f'
表示没有指数,-ddd.dddd
'g'
,对于大指数来说,'g'
表示'e'
,否则'g'
表示f
'G'
,对于大指数来说,'G'
表示'e'
,否则'G'
表示f
'x'
表示十六进制分数和二进制指数,-0xd.ddddp±ddd
'X'
表示十六进制分数和二进制指数,-0Xd.ddddP±ddd
prec是精度:对于'e', 'E','f', 'x', 和 'X',它是小数点后的位数。对于'g' 和 'G',是最大的有效数字的位数(删除了末尾的0)。-1表示使用必须的位数的最小数字。
bitSize是位的大小:32 表示 float32, 64 表示 float64
// 整型转字符串
aInt := -123
anotherStr := strconv.Itoa(aInt)
fmt.Println("整型转字符串之后的值", anotherStr)
// 字符串转bool值
aBool, _ := strconv.ParseBool("false")
// 布尔值转字符串
strA := strconv.FormatBool(false)
// 字符串转浮点数,ParseFloat的第2个参数是位的大小,32 表示 float32,64 表示 float64
aFloat, _ := strconv.ParseFloat("1.23", 64)
// 浮点数转字符串
// 'E'表示十进制指数,-1表示必须的最小位数,64表示float64
strB := strconv.FormatFloat(1.23, 'E', -1, 64)
// 字符串转无符号整型
aUint, _ := strconv.ParseUint("123", 10, 64)
//无符号整型转字符串,将10进制表示的123转换为字符串
strC := strconv.FormatUint(123, 10)
fmt.Println(aBool, strA, aFloat, strB, aUint, strC)
3. 格式化字符串
使用fmt包中的函数打印指定格式的字符串到标准输出中,以及将字符串整理为指定格式。
package main
import (
"fmt"
)
func main() {
fmt.Printf("%6.2f", 12.3) // 打印出的内容是12.30
s := fmt.Sprintf("%6.2f", 12.3)
fmt.Println(s) // 打印出的内容是12.30
}
Printf
根据指定的格式打印字符串到标准输出中。Printf
后面不会自带换行符,如果需要换行符的时候,手动在字符串末尾加上。
func Printf(format string, a ...any) (n int, err error)
Println
打印内容到标准输出中,参数之间会加上空格,末尾会加上换行符。
func Println(a ...any) (n int, err error)
Sprintf
根据指定的格式返回字符串。
func Sprintf(format string, a ...any) string
在format中的一些特殊符号的含义:
%v 值的默认格式
当打印结构体的时候,使用加号标识 (%+v)会将字段名称也打印出
%#v go语法的值的表示
%T go语法的值的类型的表示
%% 表示一个百分号符号
%t 单词true或者false
%b 二进制的数
%c 打印Unicode代码点对应的字符
%d 打印10进制数
%o 打印8进制数
%s 字符串或者切片未解析的字节
...
还有更多符号的含义,查看fmt包了解更多。
type A struct {
A1 string
}
a := A{
A1: "aaa",
}
fmt.Printf("%v\n", a) // 打印内容 {aaa}
fmt.Printf("%+v\n", a) // 打印内容 {A1:aaa}
fmt.Printf("%#v\n", a) // 打印内容 main.A{A1:"aaa"}
fmt.Printf("%T\n", a) // 打印内容 main.A
fmt.Printf("%t\n", true) // 打印内容 true
fmt.Printf("%b\n", 8) // 打印内容 1000
fmt.Printf("%c, %c, %c, %c\n", 65, 90, 97, 122) // 打印内容 A, Z, a, z
fmt.Printf("%d\n", 123) // 打印内容 123
fmt.Printf("%o\n", 123) // 打印内容 173
时间
使用time包中的函数。
1. 字符串和时间的相互转换
package main
import (
"fmt"
"time"
)
func main() {
// 时间转字符串
now := time.Now()
strA := now.Format("2006-01-02 15:04:05")
fmt.Println(strA)
// 字符串转时间
timeA, _ := time.Parse("2006-01-02 15:04:05", strA)
fmt.Println(timeA) // 打印的值是 2022-09-25 17:58:26 +0000 UTC,UTC是世界标准时间
local, _ := time.LoadLocation("Local") // 本地时区
timeB, _ := time.ParseInLocation("2006-01-02 15:04:05", strA, local)
fmt.Println(timeB) // 打印的值是 2022-09-25 17:58:26 +0800 CST,CST是中国标准时间(北京时间),比UTC晚8个小时
}
2. 时间的比较
// 时间的加减
tenMinute, _ := time.ParseDuration("-10m") // 有效的时间单位是 "ns", "us" (或者 "µs"), "ms", "s", "m", "h"。
timeC := timeA.Add(tenMinute) // 拿到距离timeA 10分钟之前的时间
duration := timeC.Sub(timeA)
// 时间的比较
fmt.Println(duration.Minutes()) // 打印的值为-10
fmt.Println(timeA.Equal(timeB)) // 判断 timeA 是否等于 timeB,值为false,timeA和timeB因为时区不同,所以这两个时间不相等
fmt.Println(timeC.Before(timeA)) // 判断 timeA 是否小于 timeB,值为true
fmt.Println(timeA.After(timeB)) // 判断 timeA 是否大于 timeB,值为true
fmt.Println(timeB.After(timeA)) // 判断 timeA 是否大于 timeB,值为false
3. 时间戳
Unix时间戳是从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数,不考虑闰秒。
package main
import (
"fmt"
"time"
)
func main() {
// 获取当前时间的时间戳
fmt.Println(time.Now().Unix())
}
切片
slice1 := []int{1, 2, 3}
fmt.Println(slice1) // [1 2 3]
slice2 := make([]int, 3) // 这里的3是数组的长度,是切片的初始长度
fmt.Println(slice2) // [0 0 0]
// 向切片中添加元素
slice2 = append(slice2, 1)
fmt.Println(slice2) // [0 0 0 1]
slice3 := make([]int, 2)
slice3 = append(slice3, []int{2, 3, 4}...)
fmt.Println(slice3) // [0 0 2 3 4]
// 获取切片的部分内容
fmt.Println(slice1[:]) // [1 2 3],slice[low:high],省略low之后low的默认值是0,省略high之后,high的默认值是切片的长度
fmt.Println(slice1[2:]) // [3]
fmt.Println(slice1[:1]) // [1]
// 将slice1中的元素复制到slice2中
copy(slice2, slice1)
fmt.Println(slice2) // [1 2 3 1]
// 遍历切片
for index, value := range slice2 {
fmt.Printf("索引%d,值%d\n", index, value)
}
var slice4 []string
fmt.Println(slice4 == nil) // true,声明的切片的默认值是nil
fmt.Println(len(slice4)) // 0,空的切片的默认长度是0
映射
map1 := map[string]string{
"a_key": "a_value",
"b_key": "b_value"}
fmt.Println(map1) // map[a_key:a_value b_key:b_value]
map2 := make(map[int]string)
fmt.Println(map2) // map[]
map3 := map[string]interface{}{
"a": []int{1, 2},
"b": 1.23,
}
fmt.Println(map3) // map[a:[1 2] b:1.23]
// 从映射中获取对应键的值
fmt.Println(map3["a"]) // [1 2]
// 修改映射中对应键的值
map3["a"] = 1
fmt.Println(map3) // map[a:1 b:1.23]
// 遍历映射
for key, value := range map3 {
fmt.Printf("键:%v, 值:%v\n", key, value)
}
var map4 map[string]int
fmt.Println(map4 == nil) // true,声明的map的默认值是nil
fmt.Println(len(map4)) // 0,空map的长度为0
写python项目的时候传递参数时使用**kwargs
有一种很方便的用法将参数的数据追加到字典中:
def a(**kwargs):
x = {
'one': '1'
}
x.update(kwargs)
print(x)
data1 = {
'two': '2',
'three': '3'
}
a(**data1) # 打印 {'one': '1', 'two': '2', 'three': '3'}
go的...参数得到的是[]T
切片类型的数据,而不是字典类型的数据,不能直接追加到映射中。
package main
import (
"fmt"
)
func a(params ...int) {
fmt.Println(params)
}
func main() {
a(1,2,3) // 打印[1 2 3]
}
所以需要这样写:
package main
import (
"fmt"
)
func a(params map[string]interface{}) {
x := map[string]interface{}{
"one": "1",
}
for key, value := range params {
x[key] = value
}
fmt.Println(x)
}
func main() {
params := map[string]interface{}{
"two": "2",
"three": "3",
}
a(params) // 打印 map[one:1 three:3 two:2]
}
接口
接口类型定义了类型的集合。一个接口对象可以存储一个值,这个值是接口的类型集合中的任意一个类型。
一个类型通过实现接口的方法的方式来实现接口。
可以通过A Tour of Go的接口部分进行练习。
package main
import (
"fmt"
)
func main() {
aPerson := Person{
Name: "沫沫",
}
fmt.Println(aPerson.Dream("梦想成为闲人")) // 沫沫梦想成为闲人
}
type Behavior interface {
Dream(content string) string
}
type Person struct {
Name string
}
// 类型Person实现了接口Behavior
func (t Person) Dream(content string) string {
return fmt.Sprintf("%s%s", t.Name, content)
}
空接口interface{}
(别名为any
)表示任意类型,对于未知类型的值可以使用空接口。
类型断言语句断言接口类型包含指定的类型。
var a interface{}
a = 1
a = "2"
fmt.Println(a) // 2
// 类型断言
var s string
s = a.(string)
s, ok := a.(string)
fmt.Println(s, ok) // 2, true
if !ok {
fmt.Println("a的值的类型不是string")
}
延迟函数
延迟函数会在函数返回之前被调用。
package main
import (
"fmt"
)
func main() {
defer func() {
fmt.Println("a")
}()
defer func() {
fmt.Println("b")
}()
defer func() {
fmt.Println("c")
}()
fmt.Println("要执行的逻辑1")
fmt.Println("要执行的逻辑2")
}
打印的内容:
要执行的逻辑1
要执行的逻辑2
c
b
a
recover
recover 可以让进入宕机流程的goroutine恢复过来;recover 只能在延迟函数defer中有效。
go没有异常捕获机制,panic类似js中的抛出异常(比如throw '某错误'
),recover类似js中的try/catch
捕获异常。
package main
func main() {
practiceRecover()
}
func practiceRecover() {
panic("出错了")
}
直接抛出错误,这个错误会导致程序宕机。
$ go run main.go
panic: 出错了
goroutine 1 [running]:
main.practiceRecover(...)
/路径/main.go:8
main.main()
/路径/main.go:4 +0x39
exit status 2
如果使用recover,程序遇到panic之后会继续执行,不会宕机。我们可以拿到错误的信息进行一些处理。
package main
import (
"fmt"
)
func main() {
practiceRecover()
}
func practiceRecover() {
defer func() {
err := recover()
fmt.Println("此处获取到的错误:", err)
}()
panic("出错了")
}
执行结果:
$ go run main.go
此处获取到的错误: 出错
指针
结构体
并发
转载自:https://juejin.cn/post/7148079720561803271