雨月烟红 發表於 2025-12-4 11:38:38

Go异常处理、泛型和文件操作实例代码

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li><a href="#_label0">一:异常处理</a></li><ul class="second_class_ul"><li><a href="#_lab2_0_0">常见的异常处理</a></li><ul class="third_class_ul"><li><a href="#_label3_0_0_0">向上抛</a></li><li><a href="#_label3_0_0_1">中断程序</a></li><li><a href="#_label3_0_0_2">恢复程序</a></li></ul></ul><li><a href="#_label1">二:泛型</a></li><ul class="second_class_ul"><li><a href="#_lab2_1_1">泛型函数</a></li><ul class="third_class_ul"></ul><li><a href="#_lab2_1_2">泛型结构体</a></li><ul class="third_class_ul"></ul><li><a href="#_lab2_1_3">泛型切片</a></li><ul class="third_class_ul"></ul><li><a href="#_lab2_1_4">泛型 map</a></li><ul class="third_class_ul"></ul></ul><li><a href="#_label2">三:文件操作</a></li><ul class="second_class_ul"><li><a href="#_lab2_2_5">文件读取</a></li><ul class="third_class_ul"><li><a href="#_label3_2_5_3">一次性读取</a></li><li><a href="#_label3_2_5_4">获取当前go文件的路径</a></li><li><a href="#_label3_2_5_5">分片读</a></li><li><a href="#_label3_2_5_6">带缓冲读</a></li><li><a href="#_label3_2_5_7">指定分割符</a></li></ul><li><a href="#_lab2_2_6">文件写入</a></li><ul class="third_class_ul"><li><a href="#_label3_2_6_8">一次性写</a></li><li><a href="#_label3_2_6_9">文件的打开方式</a></li><li><a href="#_label3_2_6_10">文件复制</a></li><li><a href="#_label3_2_6_11">目录操作</a></li></ul></ul><li><a href="#_label3">四:总结</a></li><ul class="second_class_ul"></ul></ul></div><p class="maodian"><a name="_label0"></a></p><h2>一:异常处理</h2>
<p>go的异常处理可能是这门语言唯一的一个诟病了吧</p>
<p>由于 go 语言没有捕获异常的机制,导致每调一个函数都要接一下这个函数的 error</p>
<p>网上有个梗,叫做 error 是 go 的一等公民</p>
<p class="maodian"><a name="_lab2_0_0"></a></p><h3>常见的异常处理</h3>
<p class="maodian"><a name="_label3_0_0_0"></a></p><h4>向上抛</h4>
<p>将错误交给上一级处理</p>
<p>一般是用于框架层,有些错误框架层面不能擅做决定,将错误向上抛不失为一个好的办法</p>
<div class="jb51code"><pre class="brush:go;">package main

import (
"errors"
"fmt"
)

func Parent() error {
err := method() // 遇到错误向上抛
return err
}
func method() error {
return errors.New("出错了")
}

func main() {
fmt.Println(Parent())
}
</pre></div>
<p class="maodian"><a name="_label3_0_0_1"></a></p><h4>中断程序</h4>
<p>遇到错误直接停止程序</p>
<p>这种一般是用于初始化,一旦初始化出现错误,程序继续走下去也意义不大了,还不如中断掉</p>
<div class="jb51code"><pre class="brush:go;">package main

import (
"fmt"
"os"
)

func init() {
// 读取配置文件中,结果路径错了
_, err := os.ReadFile("xxx")
if err != nil {
    panic(err.Error())
}
}

func main() {
fmt.Println("啦啦啦")
}
</pre></div>
<p class="maodian"><a name="_label3_0_0_2"></a></p><h4>恢复程序</h4>
<p>我们可以在一个函数里面,使用一个 defer,可以实现对 panic 的捕获</p>
<p>以至于出现错误不至于让程序直接崩溃</p>
<p>这种一般也是框架层的异常处理所做的</p>
<div class="jb51code"><pre class="brush:go;">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) // 肯定会有一个panic
}

func main() {

read()
}
</pre></div>
<p>当然,这个用于捕获异常的 defer 的延迟函数可以在调用链路上的任何一个函数上,但是 main中 捕获了会直接结束程序</p>
<ul><li>panic 会层层向上抛,当前函数不处理,defer 放到 main 函数中也是会被处理的</li><li>一旦 panic 发生,即使被 recover() 捕获,当前调用栈的&ldquo;正常流程&rdquo;也会中断</li></ul>
<p class="maodian"><a name="_label1"></a></p><h2>二:泛型</h2>
<p>从 1.18 版本开始,Go 添加了对泛型的支持,即类型参数</p>
<p class="maodian"><a name="_lab2_1_1"></a></p><h3>泛型函数</h3>
<p>如果我们要实现一个对int类型的求和函数</p>
<div class="jb51code"><pre class="brush:go;">func add(a, b int) int {
return a + b
}
</pre></div>
<p>但是这样写了之后,如果参数是 float 类型,int32 类型这些,就没办法使用了</p>
<p>难道要为每个类型都写一个这样的函数吗?</p>
<p>显然这就不合理</p>
<p>这个时候,泛型就上场了</p>
<div class="jb51code"><pre class="brush:go;">func add(a, b T) T {
return a + b
}

type Number interface {
        int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64
}

func plus(n1, n2 T) T {
        return n1 + n2
}
</pre></div>
<p class="maodian"><a name="_lab2_1_2"></a></p><h3>泛型结构体</h3>
<div class="jb51code"><pre class="brush:go;">package main

import (
"encoding/json"
"fmt"
)

type Response struct {
Code int    `json:"code"`
Msgstring `json:"msg"`
Data T      `json:"data"`
}

func main() {
type User struct {
    Name string `json:"name"`
}

type UserInfo struct {
    Name string `json:"name"`
    Ageint    `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
json.Unmarshal([]byte(`{"code":0,"msg":"成功","data":{"name":"枫枫"}}`), &amp;userResponse)
fmt.Println(userResponse.Data.Name)
var userInfoResponse Response
json.Unmarshal([]byte(`{"code":0,"msg":"成功","data":{"name":"枫枫","age":24}}`), &amp;userInfoResponse)
fmt.Println(userInfoResponse.Data.Name, userInfoResponse.Data.Age)
}
</pre></div>
<p class="maodian"><a name="_lab2_1_3"></a></p><h3>泛型切片</h3>
<div class="jb51code"><pre class="brush:go;">package main

type MySlice []T

func main() {
var mySlice MySlice
mySlice = append(mySlice, "枫枫")
var intSlice MySlice
intSlice = append(intSlice, 2)
}
</pre></div>
<p class="maodian"><a name="_lab2_1_4"></a></p><h3>泛型 map</h3>
<div class="jb51code"><pre class="brush:go;">package main

import "fmt"

type MyMap mapV

func main() {
var myMap = make(MyMap)
myMap["name"] = "枫枫"
fmt.Println(myMap)
}
</pre></div>
<p class="maodian"><a name="_label2"></a></p><h2>三:文件操作</h2>
<p class="maodian"><a name="_lab2_2_5"></a></p><h3>文件读取</h3>
<p class="maodian"><a name="_label3_2_5_3"></a></p><h4>一次性读取</h4>
<div class="jb51code"><pre class="brush:go;">byteData, _ := os.ReadFile("go_study/hello.txt")
fmt.Println(string(byteData))</pre></div>
<p class="maodian"><a name="_label3_2_5_4"></a></p><h4>获取当前go文件的路径</h4>
<p>可以通过获取当前 go 文件的路径,然后用相对于当前 go 文件的路径去打开文件</p>
<div class="jb51code"><pre class="brush:go;">// GetCurrentFilePath 获取当前文件路径
func GetCurrentFilePath() string {
_, file, _, _ := runtime.Caller(1)
return file
}
</pre></div>
<p class="maodian"><a name="_label3_2_5_5"></a></p><h4>分片读</h4>
<div class="jb51code"><pre class="brush:go;">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)
}
</pre></div>
<p class="maodian"><a name="_label3_2_5_6"></a></p><h4>带缓冲读</h4>
<p>按行读</p>
<div class="jb51code"><pre class="brush:go;">file, _ := os.Open("go_study/hello.txt")
buf := bufio.NewReader(file)
for {
line, _, err := buf.ReadLine()
fmt.Println(string(line))
if err != nil {
    break
}
}
</pre></div>
<p class="maodian"><a name="_label3_2_5_7"></a></p><h4>指定分割符</h4>
<div class="jb51code"><pre class="brush:go;">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())
}
</pre></div>
<p class="maodian"><a name="_lab2_2_6"></a></p><h3>文件写入</h3>
<p class="maodian"><a name="_label3_2_6_8"></a></p><h4>一次性写</h4>
<div class="jb51code"><pre class="brush:go;">err := os.WriteFile("go_study/file1.txt", []byte("这是内容"), os.ModePerm)
fmt.Println(err)
</pre></div>
<p class="maodian"><a name="_label3_2_6_9"></a></p><h4>文件的打开方式</h4>
<p>常见的一些打开模式</p>
<div class="jb51code"><pre class="brush:go;">// 如果文件不存在就创建
os.O_CREATE|os.O_WRONLY
// 追加写
os.O_APPEND|os.O_WRONLY
// 可读可写
os.O_RDWR
</pre></div>
<p>完整的模式</p>
<div class="jb51code"><pre class="brush:go;">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_TRUNCint = syscall.O_TRUNC// 打开时清空文件
)
</pre></div>
<p>文件的权限</p>
<p>主要用于linux系统,在windows下这个参数会被无视,代表文件的模式和权限位</p>
<p class="maodian"><a name="_label3_2_6_10"></a></p><h4>文件复制</h4>
<div class="jb51code"><pre class="brush:go;">io.Copy(dst Writer, src Reader) (written int64, err error)
</pre></div>
<p>将 src 文件的内容复制到 dst 文件</p>
<div class="jb51code"><pre class="brush:go;">read, _ := os.Open("go_study/file1.txt")
write, _ := os.Create("go_study/file3.txt") // 默认是 可读可写,不存在就创建,清空文件
n, err := io.Copy(write, read)
fmt.Println(n, err)
</pre></div>
<p class="maodian"><a name="_label3_2_6_11"></a></p><h4>目录操作</h4>
<div class="jb51code"><pre class="brush:go;">dir, _ := os.ReadDir("go_study")
for _, entry := range dir {
info, _ := entry.Info()
fmt.Println(entry.Name(), info.Size()) // 文件名,文件大小,单位比特
}
</pre></div>
<p class="maodian"><a name="_label3"></a></p><h2>四:总结</h2>
頁: [1]
查看完整版本: Go异常处理、泛型和文件操作实例代码