文章目录
-
- 将错误交给上一级处理 一般是用于框架层,有些错误框架层面不能擅做决定,将错误向上抛不失为一个好的办法 package main import ( “errors” “fmt” ) func Parent() error { err := method() // 遇到错误向上抛 return err } func method() error { return errors.New(“出错了”) } func main() { fmt.Println(Parent()) }
- 遇到错误直接停止程序 这种一般是用于初始化,一旦初始化出现错误,程序继续走下去也意义不大了,还不如中断掉 package main import ( “fmt” “os” ) func init() { // 读取配置文件中,结果路径错了 _, err := os.ReadFile(“xxx”) if err != nil { panic(err.Error()) } } func main() { fmt.Println(“啦啦啦”) }
- 我们可以在一个函数里面,使用一个 defer,可以实现对 panic 的捕获 以至于出现错误不至于让程序直接崩溃 这种一般也是框架层的异常处理所做的 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 的延迟函数可以在调用链路上的任何一个函数上,但是 main中 捕获了会直接结束程序 panic 会层层向上抛,当前函数不处理,defer 放到 main 函数中也是会被处理的 一旦 panic 发生,即使被 recover() 捕获,当前调用栈的“正常流程”也会中断
- 如果我们要实现一个对int类型的求和函数 func add(a, b int) int { return a + b } 但是这样写了之后,如果参数是 float 类型,int32 类型这些,就没办法使用了 难道要为每个类型都写一个这样的函数吗? 显然这就不合理 这个时候,泛型就上场了 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 }
- 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) }
- 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 文件的路径去打开文件 // 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) // 按照字节读读,中文会乱码 for scanner.Scan() { fmt.Println(scanner.Text()) }
-
- err := os.WriteFile(“go_study/file1.txt”, []byte(“这是内容”), os.ModePerm) fmt.Println(err)
- 常见的一些打开模式 // 如果文件不存在就创建 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 // 如果不存在就创建 O_EXCL int = syscall.O_EXCL // 文件必须不存在 O_SYNC int = syscall.O_SYNC // 同步打开 O_TRUNC int = syscall.O_TRUNC // 打开时清空文件 ) 文件的权限 主要用于linux系统,在windows下这个参数会被无视,代表文件的模式和权限位
- io.Copy(dst Writer, src Reader) (written int64, err error) 将 src 文件的内容复制到 dst 文件 read, _ := os.Open(“go_study/file1.txt”) write, _ := os.Create(“go_study/file3.txt”) // 默认是 可读可写,不存在就创建,清空文件 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()) // 文件名,文件大小,单位比特 }
目录
- 一:异常处理
- 常见的异常处理
- 向上抛
- 中断程序
- 恢复程序
- 二:泛型
- 泛型函数
- 泛型结构体
- 泛型切片
- 泛型 map
- 三:文件操作
- 文件读取
- 一次性读取
- 获取当前go文件的路径
- 分片读
- 带缓冲读
- 指定分割符
- 文件写入
- 一次性写
- 文件的打开方式
- 文件复制
- 目录操作
- 四:总结
go的异常处理可能是这门语言唯一的一个诟病了吧
由于 go 语言没有捕获异常的机制,导致每调一个函数都要接一下这个函数的 error
网上有个梗,叫做 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())
}
遇到错误直接停止程序
这种一般是用于初始化,一旦初始化出现错误,程序继续走下去也意义不大了,还不如中断掉
package main
import (
"fmt"
"os"
)
func init() {
// 读取配置文件中,结果路径错了
_, err := os.ReadFile("xxx")
if err != nil {
panic(err.Error())
}
}
func main() {
fmt.Println("啦啦啦")
}
我们可以在一个函数里面,使用一个 defer,可以实现对 panic 的捕获
以至于出现错误不至于让程序直接崩溃
这种一般也是框架层的异常处理所做的
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 的延迟函数可以在调用链路上的任何一个函数上,但是 main中 捕获了会直接结束程序
- panic 会层层向上抛,当前函数不处理,defer 放到 main 函数中也是会被处理的
- 一旦 panic 发生,即使被 recover() 捕获,当前调用栈的“正常流程”也会中断
从 1.18 版本开始,Go 添加了对泛型的支持,即类型参数
如果我们要实现一个对int类型的求和函数
func add(a, b int) int {
return a + b
}
但是这样写了之后,如果参数是 float 类型,int32 类型这些,就没办法使用了
难道要为每个类型都写一个这样的函数吗?
显然这就不合理
这个时候,泛型就上场了
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
}
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)
}
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 文件的路径去打开文件
// 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) // 按照字节读读,中文会乱码
for scanner.Scan() {
fmt.Println(scanner.Text())
}
err := os.WriteFile("go_study/file1.txt", []byte("这是内容"), os.ModePerm)
fmt.Println(err)
常见的一些打开模式
// 如果文件不存在就创建 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 // 如果不存在就创建 O_EXCL int = syscall.O_EXCL // 文件必须不存在 O_SYNC int = syscall.O_SYNC // 同步打开 O_TRUNC int = syscall.O_TRUNC // 打开时清空文件 )
文件的权限
主要用于linux系统,在windows下这个参数会被无视,代表文件的模式和权限位
io.Copy(dst Writer, src Reader) (written int64, err error)
将 src 文件的内容复制到 dst 文件
read, _ := os.Open("go_study/file1.txt")
write, _ := os.Create("go_study/file3.txt") // 默认是 可读可写,不存在就创建,清空文件
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()) // 文件名,文件大小,单位比特
}
到此这篇关于Go异常处理、泛型和文件操作的文章就介绍到这了,更多相关Go异常处理、泛型和文件操作内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!
您可能感兴趣的文章:
- Go语言异常处理(Panic和recovering)用法详解
- Golang错误处理:异常捕捉和恢复机制
- 一文带你掌握go中的异常处理
- 重学Go语言之错误处理与异常机制详解
- Golang泛型的使用方法详解
- 一文带你深入理解Golang中的泛型
- 一文带你了解Golang中的泛型