Golang教程五(异常处理,泛型,文件操作)

目录

一、异常处理

错误处理与异常区分

Panic 与 Recover

使用原则

二、泛型

泛型函数

 泛型结构体

泛型切片

泛型Map

三、文件操作

1.基础io

Reader interface

Writer interface

Seek interface

Close interface

文件打开模块

1.文件读取

一次性读取

获取当前go文件的路径

分片读

按缓冲读

按行读

光标操作

按分割符读

2.文件写入

文件的打开方式

文件的权限

常规写

快速写

缓冲写

3.文件复制

4.目录操作


一、异常处理

Go 语言的异常处理机制与传统的面向对象语言(如 Java、C#)所使用的 try-catch 结构有所不同,它采用了自己独特的设计理念和方法。

错误处理与异常区分

在 Go 中,错误通常用来表示预期可能会发生的运行时问题,如文件未找到、网络请求失败等。这类问题不是程序本身的严重错误,而是程序运行过程中可能遇到的常态。Go 通过返回值(通常是第二个返回值)来传递错误信息,使得函数调用者能够检查并处理这些错误。常见的做法是在函数签名中添加一个 error 类型的返回值,如:

func SomeFunction(arg string) (result int, err error) {
    // ...
    if someErrorCondition {
        return 0, errors.New("An expected error occurred")
    }
    // ...
}

调用者需要显式检查返回的 err 是否为 nil 来判断是否发生错误:

result, err := SomeFunction("input")
if err != nil {
    // Handle the error here
    log.Printf("An error occurred: %v", err)
} else {
    // Use the result
    fmt.Println(result)
}

Panic 与 Recover

Panic 是 Go 语言中用于表示异常或不可恢复的错误情况的一种机制。当程序遇到严重的、无法正常处理的问题,如内部逻辑错误、资源耗尽、不兼容的数据状态等,可以主动调用 panic() 函数来触发恐慌。恐慌会立即中断当前 goroutine(协程)的正常执行流程,并开始执行 栈展开(stack unwinding),即逐级回溯当前 goroutine 的调用栈,执行沿途遇到的 defer 语句,直到整个 goroutine 终止。

func dangerousFunction() {
    if someCriticalCondition {
        panic("A critical error occurred!")
    }
    // ...
}

 Recover 是与 panic 配合使用的另一个关键函数,它用于捕获发生在当前 goroutine 中的恐慌,并允许程序从恐慌状态中恢复过来。recover() 只能在 defer 函数中有效,因为它依赖于栈展开的过程:

func guardedFunction() {
    defer func() {
        if r := recover(); r != nil {
            // Handle the panic here
            log.Printf("Recovered from panic: %v", r)
            // Optionally, re-throw or convert to a regular error
        }
    }()
    
    dangerousFunction()
}

 在上述例子中,如果 dangerousFunction() 触发了恐慌,defer 语句中的匿名函数会被调用,此时 recover() 返回恐慌值(通常是 interface{} 类型,可以转换为适当的类型进行处理)。如果没有发生恐慌,recover() 返回 nil

使用原则

  • 错误处理 应作为程序常态的一部分,对于可预见的问题使用返回错误的方式处理,鼓励程序员显式检查并适当地处理这些错误。
  • Panic 应仅用于表示程序中的严重错误或不一致性,通常是那些不应该在正常运行时出现的情况,或者在出现时程序无法安全地继续执行的情况。
  • Recover 通常用于特定的错误边界,如服务入口点、重要组件的外部接口等,以防止局部的恐慌导致整个程序崩溃。对于大多数常规业务逻辑,直接让恐慌向上冒泡到顶层,由程序的主 goroutine 或全局的 recovery 机制来处理更为合适。
  • 使用 panic 和 recover 时应保持谨慎,过度使用可能导致代码难以理解和维护。良好的编程实践是尽可能通过返回错误来处理问题,而非频繁引发恐慌。

综上所述,Go 语言通过明确区分错误处理与异常处理,提倡简洁、显式的错误传播机制,并提供 panic 和 recover 作为处理程序运行时严重问题的手段,构建了一套独特的异常处理体系。开发者应遵循这些原则,确保程序在遇到问题时能够优雅、可控地响应。

二、泛型

Go 语言的泛型(Generics)是该语言的一个重要特性,它允许开发者编写具有类型参数的通用代码,从而提高代码的复用性、减少重复,并增强代码的可读性和安全性。Go 语言的泛型设计经过多年的讨论和迭代,在 Go 1.18 版本(2022年3月发布)中正式引入。以下是对 Go 语言泛型的关键概念、基本用法以及设计特点的概述:

泛型函数

如果我们要实现一个对int类型的求和函数

func add(a, b int) int {
  return a + b
}

但是这样写了之后,如果参数是float类型,就没办法使用了,难道要为每个类型都写一个这样的函数吗?

显然这就不合理

这个时候,泛型就上场了

func add[T int | float64 | int32](a, b T) T {
  return a + b
}

 泛型结构体

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: "os_lee",
	// },
	//}
	//byteData, _ := json.Marshal(user)
	//fmt.Println(string(byteData))
	//userInfo := Response{
	// Code: 0,
	// Msg:  "成功",
	// Data: UserInfo{
	//   Name: "os_lee",
	//   Age:  18,
	// },
	//}
	//byteData, _ = json.Marshal(userInfo)
	//fmt.Println(string(byteData))

	var userResponse Response[User]
	json.Unmarshal([]byte(`{"code":0,"msg":"成功","data":{"name":"os_lee"}}`), &userResponse)
	fmt.Println(userResponse.Data.Name)
	var userInfoResponse Response[UserInfo]
	json.Unmarshal([]byte(`{"code":0,"msg":"成功","data":{"name":"os_lee","age":18}}`), &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"] = "os_lee"
	fmt.Println(myMap)
}

三、文件操作

1.基础io

Reader interface

将len(p)个字节读取到p中

type Reader interface {
  Read(p []byte) (n int, err error)
}

Writer interface

用于将p中的数据写入到对象的数据流中 

type Writer interface {
  Write(p []byte) (n int, err error)
}

Seek interface

offset是指针移动的偏移量

whence表示指针移动的方式

  • 0 从数据的头部开始移动指针
  • 1 从数据的当前指针位置开始移动指针
  • 2 从数据的尾部移动指针

seek设置下一次读写操作的指针位置,每次的读写操作都是从指针位置开始的

type Seeker interface {
  Seek(offset int64, whence int) (int64, error)
}

Close interface

关闭文件

type Closer interface {
  Close() error
}

文件打开模块

	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   // 同步io
	O_TRUNC  int = syscall.O_TRUNC  // 打开时清空文件

1.文件读取

ioutil.ReadFile(name string) ([]byte, error)
os.ReadFile(name string) ([]byte, error)
os.Open(name string) (file *File, err error)
os.OpenFile(name string, flag int, perm FileMode) (*File, error)

一次性读取

package main

import (
	"fmt"
	"os"
)

func main() {
	bs, err := os.ReadFile("./test.txt")
	if err != nil {
		fmt.Printf("Read file error: %v\n", err)
		return
	}
	fmt.Printf("%s\n", bs)
}

获取当前go文件的路径

可以通过获取当前go文件的路径,然后用相对于当前go文件的路径去打开文件

package main

import (
	"fmt"
	"runtime"
)

// GetCurrentFilePath 获取当前文件路径
func GetCurrentFilePath() string {
	_, file, _, _ := runtime.Caller(1)
	return file
}

func main() {
	path := GetCurrentFilePath()
	fmt.Println(path)
}

分片读

package main

import (
	"fmt"
	"io"
	"os"
)

func main() {
	file, err := os.Open("./test.txt")
	if err != nil {
		panic(err)
	}
	defer file.Close()

	for {
		buf := make([]byte, 32)
		_, err = file.Read(buf)
		if err == io.EOF {
			break
		}
		if err != nil {
			panic(err)
		}
		fmt.Printf("%s", buf)
	}
}

按缓冲读

按行读
package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	file, err := os.Open("./test.txt")
	if err != nil {
		fmt.Printf("Open file error: %v\n", err)
		return
	}
	defer file.Close()

	reader := bufio.NewReader(file)
	for {
		// 按行读取
		line, _, err := reader.ReadLine()
		fmt.Println(string(line))
		if err != nil {
			break
		}
	}
}
光标操作
package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
)

func main() {
	file, err := os.Open("./test.txt")
	if err != nil {
		fmt.Printf("Open file error: %v\n", err)
		return
	}
	defer file.Close()

	// 开始位置前进5个字节
	var whence = 0
	var offset int64 = 5
	pos, _ := file.Seek(offset, whence)
	fmt.Println("Jump forward 5 bytes from start position:", pos)

	// 当前位置回退2个字节
	whence = 1
	offset = -2
	pos, _ = file.Seek(offset, whence)
	fmt.Println("Jump back 2 bytes from current position:", pos)

	reader := bufio.NewReader(file)
	for {
		// 按行读取
		line, err := reader.ReadString('\n')
		if err == io.EOF {
			break
		}
		fmt.Print(line)
	}
}
按分割符读
package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	file, err := os.Open("./test.txt")
	if err != nil {
		fmt.Printf("Open file error: %v\n", err)
		return
	}
	defer file.Close()
	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())
	}
	err1 := scanner.Err()
	if err1 != nil {
		panic(err1)
	}
}

2.文件写入

文件的打开方式

os.OpenFile(name string, flag int, perm FileMode) (file *File, err error)
os.Create(name string) (*File, error)
io/ioutil.Write(filename string, data []byte, perm fs.FileMode) error

文件操作模式:
覆盖写:os.O_WRONLY | os.O_TRUNC
追加写:os.O_WRONLY | os.O_APPEND
读写并追加:os.O_RDWR | os.OS_APPEND

完整的

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下这个参数会被无视,代表文件的模式和权限位,

三个占位符

第一个是文件所有者所拥有的权限

第二个是文件所在组对其拥有的权限

第三个占位符是指其他人对文件拥有的权限

根据UNIX系统的权限模型,文件或目录的权限模式由三个数字表示,分别代表 所有者(Owner) 、组(Group) 和 其他用户(Other) 的权限。每个数字由三个比特位组成,分别代表读、写和执行权限。因此,对于一个mode参数值,它的每一个数字都是一个八进制数字,代表三个比特位的权限组合

R:读,Read的缩写,八进制值为 4;
W:写,Write的缩写,八进制值为 2;
X:执行,Execute的缩写,八进制值为 1;

0444 表示三者均为只读的权限;
0666 表示三者均为“读写”的权限;
0777 表示三者均为读写执行的权限;
0764 表示所有者有读写执行(7=4+2+1)的权限,组有读写(6=4+2)的权限,其他用户则为只读(4=4); 

常规写

package main

import (
	"fmt"
	"os"
)

func main() {
	file, err := os.OpenFile("./test1.txt", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	byteSlice := []byte("hello world!")
	bytesWritten, err := file.Write(byteSlice)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Wrote %d bytes\n", bytesWritten)
}

快速写

package main

import "io/ioutil"

func main() {
	err := ioutil.WriteFile("abc.txt", []byte("add a new line"), 0644)
	if err != nil {
		panic(err)
	}
}

缓冲写

package main

import (
	"bufio"
	"os"
)

func main() {
	file, err := os.OpenFile("./test1.txt", os.O_CREATE|os.O_WRONLY, 0600)
	if err != nil {
		panic(err)
	}
	defer file.Close()

	msg := "Hello World!\n"

	writer := bufio.NewWriter(file)
	for i := 0; i < 5; i++ {
		writer.Write([]byte(msg))
	}
	writer.Flush()
}

3.文件复制

将src文件的内容复制到dst文件

package main

import (
	"fmt"
	"io"
	"os"
)

func main() {
	read, _ := os.Open("./file1.txt")
	write, _ := os.Create("./file3.txt") // 默认是 可读可写,不存在就创建,清空文件
	n, err := io.Copy(write, read)
	fmt.Println(n, err)
}

4.目录操作

package main

import (
	"fmt"
	"os"
)

func main() {
	dir, _ := os.ReadDir("../go_study")
	for _, entry := range dir {
		info, _ := entry.Info()
		fmt.Println(entry.Name(), info.Size()) // 文件名,文件大小,单位比特
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/549809.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

类的加载,反射和注解详解

文章目录 类的加载概述类加载器作用分类获取类加载器的方式 双亲委派机制3种加载器的关系工作机制 类加载器的应用 反射概述关键获取类对象获取构造器对象获取方法对象获取成员变量对象作用 注解概述作用自定义注解格式属性类型 元注解常见的元注解 注解解析概述方法技巧 类的加…

Qt学习记录(C++)——Day 3

目录 一、封装自定义控件 1.添加界面类 2.添加控件 3.提升封装的控件 4.实现功能 5.提供接口 6.测试接口 二、鼠标事件 前言&#xff1a; 1.鼠标进入事件 enterEvent 2.鼠标离开事件 leaveEvent 3.鼠标按下事件 mousePressEvent 4.鼠标释放事件 mouseReleaseEv…

知识跟踪模型GraphKT

1 知识跟踪Knowledge Tracing的概念 知识跟踪可以用来解决自适应学习问题。如何通过与教学材料的在线互动来有效地跟踪学生的学习进展&#xff1f;知识跟踪可用于量化学生的知识状态&#xff0c;即对教材所涉及的技能掌握水平。用于评估和模拟学生随着时间推移对技能的认知掌握…

不借助第三方工具打包QT程序

准备工作&#xff1a; 项目/可执行文件名&#xff1a;QTAppName 打包项目存放的文件名&#xff1a;pack&#xff08;这个文件名无所谓&#xff09; 脚本名&#xff1a; copylib.sh&#xff08;类似ldd命令&#xff09;&#xff1a;用于将.so库文件的依赖项复制并放入自动生…

docker拉取镜像速度慢

解决办法是配置阿里云镜像加速 在docker desktop的docker engine里添加 "registry-mirrors": ["https://owzy8hoh.mirror.aliyuncs.com"] 修改以后重启docker 参考&#xff1a; 【docker】Windows10系统下安装并配置阿里云镜像加速_docker desktop 配置…

Steam平台FPS游戏节来袭,速来免费领取头像、边框和贴纸

首先&#xff0c;活动时间从4月16日持续到4月23日&#xff0c;想领取免费物品的小伙伴们要抓紧时间啦&#xff01;领取链接就在传送门等你哦。《战地》和《使命召唤》系列没有打折哦&#xff0c;有点遗憾。不过&#xff0c;别灰心&#xff0c;这次活动还是很给力的哦&#xff0…

Hyperledger Fabric

一.Hyperledger Fabric介绍 Hyperledger区块链全家桶 Hyperledger Fabric技术特性 资产 — 资产定义使得几乎任何具有货币价值的东西都可以在网络上交 换&#xff0c;包括从食品到古董汽车再到货币期货。链码 — 链码执行与交易排序的分离&#xff0c;限制了跨节点类型所需的…

Hibernate框架

什么是Hibernate 我们可以从度娘上摘抄这样有关Hibernate的介绍&#xff1a; Hibernate是一个开放源代码的对象关系映射框架&#xff0c;它对JDBC进行了非常轻量级的对象封装&#xff0c;它将POJO(简单java对象)与数据库表建立映射关系&#xff0c;是一个全自动的orm框架&…

【nnUNetv2进阶】四、nnUNetv2 魔改网络-小试牛刀-加入注意力机制ChannelAttention

nnUNet是一个自适应的深度学习框架&#xff0c;专为医学图像分割任务设计。以下是关于nnUNet的详细解释和特点&#xff1a; 自适应框架&#xff1a;nnUNet能够根据具体的医学图像分割任务自动调整模型结构、训练参数等&#xff0c;从而避免了繁琐的手工调参过程。 自动化流程&a…

LeetCode 80—— 删除有序数组中的重复项 II

阅读目录 1. 题目2. 解题思路3. 代码实现 1. 题目 2. 解题思路 让 index指向删除重复元素后数组的新长度&#xff1b;让 st_idx 指向重复元素的起始位置&#xff0c;而 i 指向重复元素的结束位置&#xff0c;duplicate_num代表重复元素的个数&#xff1b;一段重复元素结束后&am…

入侵检测数据预处理 特征工程 面临的问题

数据预处理 对于分类任务来说,由于原始数据可能存在异常、缺失值以及不同特征的取值范围差 异大等问题,对机器学习会产生影响,因此,在进行机器学习模型训练之前,需要先对数据 进行预处理。数据预处理的主要过程包括数据清洗、去量纲、离散化等。 1.数据清洗 对采集到的数据进行…

如何制作文字gif图?一键快速生成gif闪图

大家在聊天的过程中少不了使用gif表情包&#xff0c;但是大家知道这些gif动图怎么制作的吗&#xff1f;下面就来跟大家分享一下gif动图是如何制作的吧&#xff01;其实&#xff0c;非常的简单无需下载软件只需要使用gif图片制作&#xff08;https://www.gif5.net/&#xff09;工…

QT creator 代码中有中文,提示常量中有换行符解决方案

QT creator 代码中有中文&#xff0c;提示常量中有换行符解决方案 参考视频问题问题解决 参考 感谢感谢,非常感谢,有你,让Qt不再困难,困扰我四年的问题解决了!!! https://blog.csdn.net/m0_45866718/article/details/112389513 视频 https://www.bilibili.com/video/BV1Fp4…

GitHub提交PR

本教程只做开源代码库Github工程提交pr的教程&#xff0c;不做其他的深入的讲解 Github和Gitlab的操作类似&#xff0c;只不过Github叫PR&#xff0c;GitLab叫MR&#xff0c;基本上做法是一致的 以开源项目QuickChat为例 https://github.com/Binx98/QuickChat https://github…

CAN网络管理(网络节点)

什么是CAN的网络节点 网络节点是指连接到CAN总线上的设备或模块,每个网络节点都具有唯一的标识符,称为节点ID,用于在CAN总线上进行通信和识别。 如何判断CAN的网络节点是多少 可以根据DBC来定义查看, 以ADCU为例,域控作为主节点,一般外部的像雷达,camera的数据都是向…

Yolo-world使用

1、安装 python pip install ultralytics 前往官网下载模型&#xff1a;https://docs.ultralytics.com/models/yolo-world/#key-features 我这里使用yolov8s-world.pt举例 最简单的使用示例 if __name__ __main__:model YOLO(model/yolov8s-world.pt)results model.pre…

JCVI-筛选blast最佳结果(生物信息学工具-015)

通常&#xff0c;大家会问我们经过了NR注释&#xff0c;SwissProt注释&#xff0c;那么如何进行&#xff0c;如何挑选最佳比对结果&#xff1f; 同理&#xff0c;存在一个问题&#xff0c;如何挑选最佳的blast比对结果&#xff1f;什么事最优的同源序列&#xff1f; 唐海宝老…

DBUtils工具类的使用

1、DBUtils是什么 为了更加简单地使用JDBC&#xff0c;Apache组织提供了一个DBUtils工具&#xff0c;它是操作数据库的一个组件&#xff0c;实现了对JDBC的简单封装&#xff0c;可以在不影响数据库访问性能的情况下简化JDBC的编码工作量。DBUtils工具要有2个作用。 写数据&am…

力扣周赛392复盘

3105. 最长的严格递增或递减子数组 题目 给你一个整数数组 nums 。 返回数组 nums 中 严格递增 或 严格递减 的最长非空子数组的长度。 思考&#xff1a; 返回什么&#xff1a;返回最长非空子数组的长度。return max(decs_len,incs_len); 但实际上我们只需要用一个变量ans就…

记录PS学习查漏补缺

PS学习 PS学习理论快捷键抠图PS专属多软件通用快捷键 PS学习 理论 JPEG &#xff08;不带透明通道&#xff09; PNG (带透明通道) 快捷键 抠图 抠图方式 魔棒工具 反选选中区域 CtrlShiftI&#xff08;反选&#xff09; 钢笔抠图注意事项 按着Ctrl单击节点 会出现当前节…
最新文章