这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天
前言
大家好呀,这是我参加青训营伴学笔记创作活动的第 3 天,如存在问题,烦请各位斧正!
注意:有一点要说明,时间处理和数字解析那一块,我放进去的图片说是超过了最大字符限制,所以无法展示了,如有需要,请各位与我联系。
常用基本API
基本错误处理
Go 语言通过 内置 的错误接口提供了非常简单的错误处理机制。
1、error
1)error 类型 是一个内置的 接口类型 ,这是它在源码中的定义:
type error interface {
Error() string
}
2)这意味着只要实现了这个方法,就是实现了一个Error。直接打印实现error接口的结构体变量出来的就是异常信息。
3)动态的错误信息:我们在实现error接口时,可以将结构体中定义一个info变量,动态的去改变他,Error方法返回这个info变量。
4)还可以声明一个函数,传入字符串,返回一个我们自定义的错误类型:
func New(text string) error {
return &errorString{text}
}
2、使用 Go 的内置函数生成错误信息
1)首先要"import “errors"
2)使用:
func New(text string) error3)我们定义的函数可以另外设置一个error返回值,如"
func Sqrt(f float64) (float64, error) { }"出错时返回错误,没有出错时返回nil,调用方做判断。4)判断是否为特定错误:
if err == ErrNotFound5)fmt.Errorf() :格式化控制错误信息
fmt.Errorf("%s", "error")等价于err2 := errors.New(fmt.Sprintf("%s", "error"))
错误处理进阶
1、背景:
1)在 Go 源码目录的 errors 目录下,有errors.go和wrap.go两个文件,以及对应的errors_test.go和wrap_test.go两个单元测试文件。
"要学一个新东西时,其实看xxx_test.go是一个非常不错的选择,它会告诉你这些函数该怎么用。"
2)异常链:为了更好的处理异常,我们可以让新异常指向引发它的老异常,形成异常链路。
2、errors.Unwrap函数(
func Unwrap(err error) error)1)它会判断error是否实现了下面的接口,若实现了则返回它的Unwrap方法,否则返回nil。
type Wraper interface {
Unwrap() error
}
2)注意:上述接口是在函数内使用的匿名接口,不是Go的内置接口。
3、errors.Is函数(
func Is(err, target error) bool)1)Is用来判断err错误链中是否有错误和target匹配,错误链由Unwrap函数得到。
2)Is函数中也有一个匿名接口
interface{ Is(error) bool },可以通过实现它让is函数直接返回true。4、errors.As函数(
func As(err error, target interface{}) bool)1)寻找错误链中第一个和target匹配的error,如果能找到,将err的值设置到target并返回true。
2)如果err可以赋值给target 或者 err有As(实现了匿名接口)函数并且As函数返回true,则err和target是匹配的。
interface{ As(interface{}) bool }
运行时重大错误-panic和recover
前言:go语言中没有try...catch语句。因为go语言的作者认为将异常和控制语句混在一起,很容易让这个程序变得混乱,异常也很容易被滥用。
1、panic
1)何时发生panic
如运行时出现索引越界等问题,或者主动调用panic("传入错误信息")
2)panic产生后的结果
将panic之前定义的索引defer逆序运行,然后返回上一级调用,再逆序调用defer,直到main的defer都执行完。
接下来打印panic的错误信息,会有产生的错误类型、产生位置等信息,比如:
2、recover
1)recover的作用是捕获panic,从而恢复正常代码执行。
2)它必须配合defer使用,因为panic时从头到尾返回才有机会在调用defer函数时使用recover函数捕获到。
3)recover没有传入参数,但是有返回值,返回值就是panic传递的值。
4)基本使用:
strings包
1、判断一个字符串是否在另一个字符串中
contains := strings.Contains("substr", "s") // 返回boolean2、判断一个字符串是否存在另一个字符串的任意字符
any := strings.ContainsAny("kaka", "nihao") // 返回boolean3、把一个字符串转小写
lower := strings.ToLower("KAKA") // 返回转换后的string4、把一个字符串转大写
upper := strings.ToUpper("kaka")5、判断两个字符串是否相同(无视大小写)
equal := strings.EqualFold("hello", "Hello")6、判断是否含有前缀,区分大小写
prefix := strings.HasPrefix("hello", "he") // 右是否为左的前缀7、判断是否含有后缀,区分大小写
suffix := strings.HasSuffix("hello", "llo") // 右是否为左的后缀8、统计子串出现的次数
count := strings.Count("hello", "l")9、判断一个字符串在另一个字符串第一次出现的位置,不存在返回-1
index := strings.Index("Hello World", "or") // 从0开始10、判断一个字符串中的任意一个字符在另一个字符串第一次出现的位置,不存在返回-1
indexany := strings.IndexAny("hello", "abcde")11、判断一个字符串最后出现的位置,不存在返回-1
lastindex := strings.LastIndexAny("hello", "l")12、替换
replace := strings.Replace("hello", "h", "k", -1)13、去掉前后端的指定字符
trim := strings.Trim("!!hel!lo!", "!") // 结果为hel!lo,即将前缀的两个!都去掉了14、去掉指定前缀
trimprefix := strings.TrimPrefix("hello", "he")15、去掉指定后缀
trimsuffix := strings.TrimSuffix("hello", "llo")16、按照空格切分字符串,返回切片
field := strings.Fields("i am jack") // 即使多个空格,返回的切片也不含空格[i am jack]17、按照指定字符分割字符串,返回切片
split := strings.Split("hello,i am jack", ",")18、按照指定字符分割字符串,分割多少份,返回切片
splitN := strings.SplitN("hello,i,am,jack", ",", 2)20、将字符串切片合并成一个字符串,中间插入指定分隔符
join := strings.Join([]string{"I", "am", "jack"}, ",")21、拼接字符串(使用Builder)
var build strings.Builder
build.WriteString("abc")
build.WriteByte(65)
fmt.Println(build.String())
//abcA
fmt包
1、背景:是一个用于输入输出常用的库,
在fmt包,有关格式化输入输出的方法就有两大类:scan和print,分别在scan.go和print.go文件中。
2、print.go文件中定义了9个函数:
1)按后缀划分:print为一般输出,如果后缀是"f",则指定了format,即格式化文本。如果后缀是"ln"、则有换行符。
2)按前缀划分:不同前缀代表的是输出内容的目标(终端)。如果前缀是" F ", 则指定了 io.Writer,如果前缀是" S ", 则是输出到字符串。
Print、Printf、Println // 输出内容到标准输出os.Stdout;
Fprint、Fprintf、Fprintln // 输出内容到指定的io.Writer;
Sprint、Sprintf、Sprintln // 输出内容到字符串
3、scan.go文件中定义了9个函数:
1)按后缀划分:
Scanln、Fscanln、Sscanln // 读取到换行时停止,并要求一次提供一行所有条目;
Scan、Fscan、Sscan // 读取内容时不关注换行;
Scanf、Fscanf、Sscanf // 根据格式化文本读取。
2)按前缀划分
Scan、Scanf、Scanln // 从标准输入os.Stdin读取文本;
Fscan、Fscanf、Fscanln // 从指定的io.Reader接口读取文本;
Sscan、Sscanf、Sscanln // 从一个参数字符串读取文本。
待补充的一些常用API
json处理
1、引入包:encoding/json
2、基本使用:
1)序列化:想要序列化的结构体,在声明时,需要让它每个属性名首字母都大写即可(即公开字段)。
(1)此时这个结构体的实例a就可以用json.Marshal(a)序列化为byte数组:
buf, err := json.Marshal(a)(2)如果想让序列化的json中的字段名跟属性名不一致,则可以这样声明结构体中的属性:
Age int `json:"age"`
2)打印序列化后的byte数组,需要配合string函数:fmt.println(string(buf))
3)反序列化使用 json.Unmarshal:
var temp 某结构体
err = json.Unmarshal(buf, &b)
时间处理
1、引入包:time
2、基本使用(如图,从上至下讲解)
1)获取当前时间
now := time.Now()2)使用time.Date()构造一个带时区的时间
3)使用t.Year()等方法可以获取某一个时间信息。
4)使用diff := t2.Sub(t)可以让两个时间相减。
获取到的diff也是一个时间对象,仍可以使用diff.Minutes()等方法获取某一个时间信息。
5)自定义时间格式:
t.Format()和time.Parse()其中2006-01-02那个是默认的日期时间格式的实例,即不使用yyyy-mm-dd,而是使用这个格式。
6)获取当前时间戳:
now.Unix(),使用刚才的当前时间对象获取
数字解析
1、引入包:strconv
2、基本使用:
1)ParseXX,将字符串解析为某个数值类型,int额外的第一个参数为x进制,若传入0表示自动推测。
第二个参数是错误信息。
2)使用Atoi默认转换为十进制整数:
进程信息
1、引入包:os和os/exec
2、基本使用:
1)使用
os.Getenv("Key")和os.Setenv("Key", "Value")来获取和写入环境变量。
随机数
1、math/rand包 // 伪随机
1)math/rand伪随机生成的数字是确定的,为了尽量随机性,那么我们可以每次使用不同的seed来启动程序,可以保证每次启动都产生新的随机数,一般使用时间戳。
2)示例代码:
rand.Seed(time.Now().UnixNano())
fmt.Println(rand.Intn(100)) // 随机生成100以内的正整数2、crypto/rand包 // 真随机
1)真随机的数字返回,但性能比较慢。比上一个慢10倍以上。
2)主要import "crypto/rand"和"encoding/binary"
var n int32
binary.Read(rand.Reader, binary.LittleEndian, &n) // 写入变量n