Go異常處理、泛型和文件操作實例代碼
一:異常處理
go的異常處理可能是這門語言唯一的一個詬病了吧
由于 go 語言沒有捕獲異常的機制,導(dǎo)致每調(diào)一個函數(shù)都要接一下這個函數(shù)的 error
網(wǎng)上有個梗,叫做 error 是 go 的一等公民
常見的異常處理
向上拋
將錯誤交給上一級處理
一般是用于框架層,有些錯誤框架層面不能擅做決定,將錯誤向上拋不失為一個好的辦法
package main
import (
"errors"
"fmt"
)
func Parent() error {
err := method() // 遇到錯誤向上拋
return err
}
func method() error {
return errors.New("出錯了")
}
func main() {
fmt.Println(Parent())
}
中斷程序
遇到錯誤直接停止程序
這種一般是用于初始化,一旦初始化出現(xiàn)錯誤,程序繼續(xù)走下去也意義不大了,還不如中斷掉
package main
import (
"fmt"
"os"
)
func init() {
// 讀取配置文件中,結(jié)果路徑錯了
_, err := os.ReadFile("xxx")
if err != nil {
panic(err.Error())
}
}
func main() {
fmt.Println("啦啦啦")
}
恢復(fù)程序
我們可以在一個函數(shù)里面,使用一個 defer,可以實現(xiàn)對 panic 的捕獲
以至于出現(xiàn)錯誤不至于讓程序直接崩潰
這種一般也是框架層的異常處理所做的
package main
import (
"fmt"
"runtime/debug"
)
func read() {
defer func() {
if err := recover(); err != nil {
fmt.Println(err) // 捕獲異常,打印錯誤信息
// 打印錯誤的堆棧信息
s := string(debug.Stack())
fmt.Println(s)
}
}()
var list = []int{2, 3}
fmt.Println(list[2]) // 肯定會有一個panic
}
func main() {
read()
}
當然,這個用于捕獲異常的 defer 的延遲函數(shù)可以在調(diào)用鏈路上的任何一個函數(shù)上,但是 main中 捕獲了會直接結(jié)束程序
- panic 會層層向上拋,當前函數(shù)不處理,defer 放到 main 函數(shù)中也是會被處理的
- 一旦 panic 發(fā)生,即使被 recover() 捕獲,當前調(diào)用棧的“正常流程”也會中斷
二:泛型
從 1.18 版本開始,Go 添加了對泛型的支持,即類型參數(shù)
泛型函數(shù)
如果我們要實現(xiàn)一個對int類型的求和函數(shù)
func add(a, b int) int {
return a + b
}
但是這樣寫了之后,如果參數(shù)是 float 類型,int32 類型這些,就沒辦法使用了
難道要為每個類型都寫一個這樣的函數(shù)嗎?
顯然這就不合理
這個時候,泛型就上場了
func add[T int | float64 | int32](a, b T) T {
return a + b
}
type Number interface {
int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64
}
func plus[T Number](n1, n2 T) T {
return n1 + n2
}
泛型結(jié)構(gòu)體
package main
import (
"encoding/json"
"fmt"
)
type Response[T any] struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data T `json:"data"`
}
func main() {
type User struct {
Name string `json:"name"`
}
type UserInfo struct {
Name string `json:"name"`
Age int `json:"age"`
}
//user := Response{
// Code: 0,
// Msg: "成功",
// Data: User{
// Name: "楓楓",
// },
//}
//byteData, _ := json.Marshal(user)
//fmt.Println(string(byteData))
//userInfo := Response{
// Code: 0,
// Msg: "成功",
// Data: UserInfo{
// Name: "楓楓",
// Age: 24,
// },
//}
//byteData, _ = json.Marshal(userInfo)
//fmt.Println(string(byteData))
var userResponse Response[User]
json.Unmarshal([]byte(`{"code":0,"msg":"成功","data":{"name":"楓楓"}}`), &userResponse)
fmt.Println(userResponse.Data.Name)
var userInfoResponse Response[UserInfo]
json.Unmarshal([]byte(`{"code":0,"msg":"成功","data":{"name":"楓楓","age":24}}`), &userInfoResponse)
fmt.Println(userInfoResponse.Data.Name, userInfoResponse.Data.Age)
}
泛型切片
package main
type MySlice[T any] []T
func main() {
var mySlice MySlice[string]
mySlice = append(mySlice, "楓楓")
var intSlice MySlice[int]
intSlice = append(intSlice, 2)
}
泛型 map
package main
import "fmt"
type MyMap[K string | int, V any] map[K]V
func main() {
var myMap = make(MyMap[string, string])
myMap["name"] = "楓楓"
fmt.Println(myMap)
}
三:文件操作
文件讀取
一次性讀取
byteData, _ := os.ReadFile("go_study/hello.txt")
fmt.Println(string(byteData))獲取當前go文件的路徑
可以通過獲取當前 go 文件的路徑,然后用相對于當前 go 文件的路徑去打開文件
// GetCurrentFilePath 獲取當前文件路徑
func GetCurrentFilePath() string {
_, file, _, _ := runtime.Caller(1)
return file
}
分片讀
file, _ := os.Open("go_study/hello.txt")
defer file.Close()
for {
buf := make([]byte, 1)
_, err := file.Read(buf)
if err == io.EOF {
break
}
fmt.Printf("%s", buf)
}
帶緩沖讀
按行讀
file, _ := os.Open("go_study/hello.txt")
buf := bufio.NewReader(file)
for {
line, _, err := buf.ReadLine()
fmt.Println(string(line))
if err != nil {
break
}
}
指定分割符
file, _ := os.Open("go_study/hello.txt")
scanner := bufio.NewScanner(file)
scanner.Split(bufio.ScanWords) // 按照單詞讀
//scanner.Split(bufio.ScanLines) // 按照行讀
//scanner.Split(bufio.ScanRunes) // 按照中文字符讀
//scanner.Split(bufio.ScanBytes) // 按照字節(jié)讀讀,中文會亂碼
for scanner.Scan() {
fmt.Println(scanner.Text())
}
文件寫入
一次性寫
err := os.WriteFile("go_study/file1.txt", []byte("這是內(nèi)容"), os.ModePerm)
fmt.Println(err)
文件的打開方式
常見的一些打開模式
// 如果文件不存在就創(chuàng)建 os.O_CREATE|os.O_WRONLY // 追加寫 os.O_APPEND|os.O_WRONLY // 可讀可寫 os.O_RDWR
完整的模式
const ( O_RDONLY int = syscall.O_RDONLY // 只讀 O_WRONLY int = syscall.O_WRONLY // 只寫 O_RDWR int = syscall.O_RDWR // 讀寫 O_APPEND int = syscall.O_APPEND // 追加 O_CREATE int = syscall.O_CREAT // 如果不存在就創(chuàng)建 O_EXCL int = syscall.O_EXCL // 文件必須不存在 O_SYNC int = syscall.O_SYNC // 同步打開 O_TRUNC int = syscall.O_TRUNC // 打開時清空文件 )
文件的權(quán)限
主要用于linux系統(tǒng),在windows下這個參數(shù)會被無視,代表文件的模式和權(quán)限位
文件復(fù)制
io.Copy(dst Writer, src Reader) (written int64, err error)
將 src 文件的內(nèi)容復(fù)制到 dst 文件
read, _ := os.Open("go_study/file1.txt")
write, _ := os.Create("go_study/file3.txt") // 默認是 可讀可寫,不存在就創(chuàng)建,清空文件
n, err := io.Copy(write, read)
fmt.Println(n, err)
目錄操作
dir, _ := os.ReadDir("go_study")
for _, entry := range dir {
info, _ := entry.Info()
fmt.Println(entry.Name(), info.Size()) // 文件名,文件大小,單位比特
}
四:總結(jié)
到此這篇關(guān)于Go異常處理、泛型和文件操作的文章就介紹到這了,更多相關(guān)Go異常處理、泛型和文件操作內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
GoLang調(diào)用鏈可視化go-callvis使用介紹
與鏈路追蹤(Tracing)不同,Tracing關(guān)注復(fù)雜的分布式環(huán)境中各個服務(wù)節(jié)點間的調(diào)用關(guān)系,主要用于服務(wù)治理。而我們本次探索的代碼調(diào)用鏈路則是代碼方法級別的調(diào)用關(guān)系,主要用于代碼設(shè)計2023-02-02

