Go红队开发—语法补充

文章目录

  • 错误控制
    • 使用
    • 自定义错误类型
    • 错误包装
    • errors.Is 和 errors.As
    • panic捕获、recover 、defer
    • 错误控制练习
  • 接口
    • 结构体实现接口
    • 基本类型实现接口
    • 切片实现接口
  • 接口练习
  • Embed嵌入文件

之前有师傅问这个系列好像跟红队没啥关系,前几期确实没啥关系,因为这都是进行红队工具开发的前置知识点,对于我个人强迫症而言只是想让这个系列更加完善而已,所以前置知识也加进去了,有GO知识的大佬可以等下一期哈!感谢支持。

错误控制

使用

1. errors.New("错误信息")   //这个属于error类型
例子://你会看到error返回类型,return 0, errors.New("除数不能为零") 
func divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("除数不能为零") }
return a / b, nil
}2.fmt.Errorf:错误包装,后面会详细讲。这个函数允许你使用格式化字符串创建错误,类似于 fmt.Sprintf。
这个没啥好说的,只是说New的时候只能是字符串,而你用这个就能够格式化字符串,将变量放在里面格式化输出在错误中。
err := fmt.Errorf("invalid argument: %v", value)3.errors.Is:判断错误链中是否包含该错误
if errors.Is(err, myError) {// err 是 myError自定义
}

自定义错误类型

自定义错误类型:通过实现 error 接口来自定义错误类型。

示例代码:

package mainimport "fmt"type MyError struct {When stringWhat string
}func (e *MyError) Error() string {return fmt.Sprintf("在 %s 发生 %s ", e.When, e.What)
}
func run() error {return &MyError{When: "运行时",What: "错误1",}
}
func main() {err := run()if err != nil {fmt.Println(err)}
}

注意理解具体执行过程:

  1. run() 返回一个 error 接口值,其动态类型为 *MyError,动态值为 &MyError{}

  2. 当执行

    fmt.Println(err)
    

    时:

    • err.Error() 被隐式调用。
    • 动态类型 *MyErrorError() 方法被执行,返回一个字符串。

错误包装

其实很好理解,就是使用fmt.Errorf("获取数据失败: %w", err),这相当于用获取数据失败:这个字符串包了一下err

所以fmt.Errorf("获取数据失败: %w", err)解包的时候就是等于err,因为后面的Is(is)就是为啥能判断错误链里是否包含的。

package mainimport ("errors""fmt"
)// 定义一个自定义错误
var ErrNotFound = errors.New("资源未找到")func fetchData(id int) error {if id == 0 {return ErrNotFound // 返回原始错误}return nil
}func main() {// 调用 fetchData 并包装错误err := fetchData(0)if err != nil {// 使用 fmt.Errorf 包装原始错误,添加上下文wrappedErr := fmt.Errorf("获取数据失败: %w", err)fmt.Println(wrappedErr) // 打印包装后的错误信息// 检查包装的错误是否包含特定错误if errors.Is(wrappedErr, ErrNotFound) {fmt.Println("错误类型: 资源未找到")}// 解包原始错误unwrappedErr := errors.Unwrap(wrappedErr)fmt.Printf("解包后的错误: %v\n", unwrappedErr)}
}

输出如下:(看下面的输出就知道什么情况了)

获取数据失败: 资源未找到
错误类型: 资源未找到
解包后的错误: 资源未找到

errors.Is 和 errors.As

了解了错误包装之后就知道这两的区别了,Is就是判断错误链中是否包含你这个错误,只要包含一个即可。

As就是只判断当前的,不管你是否包含的。

package mainimport ("errors""fmt"
)var ErrDivideByZero = errors.New("除数不能为零")func divide(a, b int) (int, error) {if b == 0 {return 0, ErrDivideByZero}return a / b, nil
}
func main() {_, err := divide(4, 0)if errors.Is(err, ErrDivideByZero) { //可以是ErrDivideByZero错误的错误链,因为有可能是进行了错误包装fmt.Println("捕获到除以零的错误")} else {fmt.Println("其他错误:", err)}if errors.As(err, &ErrDivideByZero) {  //一定要是ErrDivideByZero错误,同时要是一个指针,源码的painc写着target must be a non-nil pointerfmt.Println("捕获到除以零的错误")} else {fmt.Println("其他错误:", err)}
}

panic捕获、recover 、defer

解释:

  • panic 能够改变程序的控制流,调用 panic 后会立刻停止执行当前函数的剩余代码,并在当前 Goroutine 中递归执行调用方的 defer
  • recover 可以中止 panic 造成的程序崩溃。它是一个只能在 defer 中发挥作用的函数,在其他作用域中调用不会发挥作用;

注意:recover 只能在 defer 函数中调⽤,并且只有在 panic 发⽣时才会返回⾮ nil 值。

//恐慌捕获 panic recover defer
func start_panic() {defer func() {//使用defer来等待后面panic执行一个panic后再进行捕获if r := recover(); r != nil {fmt.Println("捕获到了:", r)}}()panic("一个panic")
}func main() {fmt.Println("开始捕获panic")start_panic()fmt.Println("结束捕获panic")
}

错误控制练习

固定打开一个1.txt文件,然后使用自定义错误类型,同时进行panic捕获,输出预期:错误,非预期:错误

type FileNotFoundError struct {filename string
}func (f FileNotFoundError) Error() string {return fmt.Sprintf("文件 %s 不存在。", f.filename)
}func readfile(file_name string) (string, error) {//panic一下defer func() {if r := recover(); r != nil {fmt.Println("panic is ", r)}}()if file_name != "1.txt" {return "", FileNotFoundError{filename: file_name}}bytes, err := os.ReadFile("1.txt")if err != nil {return "", fmt.Errorf("文件 %s 打开出错", file_name)} else {return string(bytes), nil}
}func main() {file, err := readfile("1.txt")if err != nil {if errors.As(err, FileNotFoundError{}) {fmt.Println("预期错误:", err)} else {fmt.Println("非预期错误:", err)}return}fmt.Println("文件内容为:", file)
}

接口

接口在go语言中也有点抽象,对于接口的实现其实很简单,只是在用的时候比较抽象,结构体实现接口反而是最容易接受的,像基本类型还有切片这两种接口的使用就比较抽象。

结构体实现接口

(在结构体实现结构以及方法的调用基本没啥问题,很正常的操作)

package mainimport "fmt"type User interface {getName() stringgetAge() int
}type Person struct {name stringage  int
}func (p Person) getName() string {return p.name
}
func (p Person) getAge() int {return p.age
}func main() {p := Person{name: "zhangsan",age:  18,}fmt.Println(p.getName(), p.getAge())
}

基本类型实现接口

这里我分两种情况,string和int实现接口,目的是了解在实现接口后怎么使用这个方法。

type Stringer interface {
//接口的这两个方法写了之后就要实现。所以下面就实现了String() stringAscii() string
}
type MyString string //string类型
type MyInt int       //int类型func (s MyString) String() string {return string(s) //其实可以不转string也能直接返回,因为MyString本身就是string类型,只是我换了个别名而已
}
func (t MyInt) Ascii() string {return string(t) //一定要转,因为本身是int类型
}
func (t MyInt) String() string {return fmt.Sprintf("%d", t) //一定要转,因为本身是int类型
}func main() {//结构体实现接口效果//start_struct_interface()var s Stringer = MyString("传递参数string")  var i Stringer = MyInt(97)fmt.Println(s.String())fmt.Println(i.Ascii())  //将数字转为asciifmt.Println(i.String()) //将数字作为字符串输出
}

细节:

实际操作下来其实也没有说很难理解,只是可能类型转换那个地方卡了一下导致难以理解而已

重点是看懂这里的代码:
var s Stringer = MyString("传递参数string")  
var i Stringer = MyInt(97)
接口类型接收实现了接口方法的类型,然后就能够调用接口方法了,就这么理解就行了。在MyString和MyInt中都是强制类型转换,将string字符和int数字转为对应的别名,然后给到变量后就能直接使用实现了接口的方法,因为已经转为了那两个基本类型了。
在强调一遍:本身是没有我们定义的这种类型的,所以要强制转换,然后接口其实就能随便写了,管你要不要用他这个值。
条件:
属于这个类型(强制类型转换)
实现了接口方法(正常实现接口方法)
调用就直接调用即可(正常)

切片实现接口

其实到了切片实现接口就很容易理解了

我发现其实是你这个类型实现了这个接口后,就可以用了

我发现要用这个方法只是仅仅的需要你是这个类型,而不是说在于什么强制类型转换,而是你要用这个接口方法是因为那个类型实现了这个接口啊,所以你要强制类型转换,所以要实例化这个类型啊,确实有点悟道了,也有点不明白我之前到底为啥会卡住。

切片实现接口也很简单,到这里其实已经不分什么结构体、基本类型、切片的了,本质就是你实例化一个实现了接口的类型,然后你的某个类型实现了接口类型的方法,那么你就直接实例化给到接口类型就拿这个类型去调用方法就行了。 (有点抽象,还是直接看代码吧)

type I_slice interface { //接口类型sum() int //返回切片的和
}
type MySlice []int //切片类型,换一个intslice别名而已,方便自定义,且实现接口方法func (ms I_slice) sum() int { //就是自己定义的类型实现了接口类型而已,很好理解s := 0for _, v := range ms {s += v}return s
}func main() {var s I_slice = MySlice{1, 2, 3, 4, 5} //我们自己自定义的切片类型然后赋值给接口类型//var s MySlice = MySlice{1, 2, 3, 4, 5} //var s = MySlice{1, 2, 3, 4, 5} //这两其实也可以,就是我们自定义的类型本来就是有实现这个方法的,只不过你没有赋值给接口类型,所以不算实现了接口的方法而已,但是你本身就拥有的方法当然可以用啦fmt.Println(s.sum())
}

接口练习

项⽬描述

创建⼀个形状计算器,它可以计算不同形状的⾯积。需要定义⼀个 Shape 接⼝,
并为不同的形状(如圆形和矩形)实现这个接⼝。
然后编写⼀个函数来计算并打印这些形状的⾯积。 步骤
定义⼀个 Shape 接⼝,包含⼀个 area ⽅法。 
创建⼀个 Circle 结构体,并实现 Shape 接⼝。 
创建⼀个 Rectangle 结构体,并实现 Shape 接⼝。 
编写⼀个函数 printArea,接受⼀个 Shape 类型的参数,并打印它的⾯积。 
在 main 函数中创建⼀些 Circle 和 Rectangle 实例,并调⽤ printArea 函数来打印它们的⾯积。

示例代码

type Shape interface {Area() float64
}type Circle struct {radius float64
}type Rectangle struct {width  float64height float64
}func (c Circle) Area() float64 {return (c.radius * c.radius * math.Pi)
}
func (r Rectangle) Area() float64 {return (r.width * r.height)
}func printArea(s Shape) {/*这里实际上就相当于强制类型转换了,因为两个结构体实现了Area方法,那么就强制类型转换为Shape后能够在函数中正常使用该方法*/fmt.Printf("%.2f\n", s.Area())
}
func main() {dc := Circle{radius: 2}r := Rectangle{width: 2, height: 4}printArea(c)printArea(r)
}

Embed嵌入文件

只支持嵌入为string, byte切片和embed.FS三种类型。相对来说比较难理解的是embed.FS

使⽤//go:embed后,下⽅必须是全局变量

使用embed可以嵌⼊⽂件夹下的⽂件,使⽤通配符*,比如static/*

示例代码:

image-20241122201153813

package mainimport ("embed"_ "embed""fmt"
)//go:embed a.txt
var a string//go:embed static/1.txt
var s []byte//go:embed static/*
var f1 embed.FS//go:embed static/* static/2.txt
var f2 embed.FS//go:embed static/1.txt
//go:embed static/2.txt
//go:embed static/3.txt
var f3 embed.FSfunc main() {fmt.Println("============string接收================")fmt.Println(a)fmt.Println("============byte接收================")fmt.Printf("%q\n", s)fmt.Println(string(s))fmt.Println("============FS单个文件================")data, _ := f1.ReadFile("static/1.txt")fmt.Println(string(data))fmt.Println("============FS目录,当前目录等等多个文件,go:embed空格隔开================")data, _ = f2.ReadFile("static/2.txt")fmt.Println(string(data))fmt.Println("============FS多个文件,go:embed可以不用空格隔开================")data, _ = f3.ReadFile("static/3.txt")fmt.Println(string(data))
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux--多进程开发(5)--进程间通信(IPC)、linux间通信的方式、管道

进程间通讯概念 每两个进程之间都是独立的资源分配单元,不同进程之间不能直接访问另一个进程的资源。 但不同的进程需要进行信息的交互和状态的传递等,因此需要进程间通信(IPC,inter processes cimmunication) 进程通信的目的: …

Uniapp开发微信小程序插件的一些心得

一、uniapp 开发微信小程序框架搭建 1. 通过 vue-cli 创建 uni-ap // nodejs使用18以上的版本 nvm use 18.14.1 // 安装vue-cli npm install -g vue/cli4 // 选择默认模版 vue create -p dcloudio/uni-preset-vue plugindemo // 运行 uniapp2wxpack-cli npx uniapp2wxpack --…

RabbitMQ 的介绍与使用

一. 简介 1> 什么是MQ 消息队列(Message Queue,简称MQ),从字面意思上看,本质是个队列,FIFO先入先出,只不过队列中存放的内容是message而已。 其主要用途:不同进程Process/线程T…

对比Grok3 普通账户与 30 美元 Super 账户:默认模式、Think 和 DeepSearch 次数限制以及如何升级

面对这个马斯克旗下的"最聪明"的人工智能,很多人都不知道他们的基本模式,本期将从几个方面开始说明: Grok3的背景与功能 账户类型及其详细背景 使用限制 使用限制对比表 如何充值使用 Super 账户 纯干货,带你了解…

【含文档+PPT+源码】基于过滤协同算法的旅游推荐管理系统设计与实现

项目介绍 本课程演示的是一款基于过滤协同算法的旅游推荐管理系统设计与实现,主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 1.包含:项目源码、项目文档、数据库脚本、软件工具等所有资料 2.带你从零开始部署运行本套系…

牛客NC288803 和+和

​import java.util.Comparator;import java.util.PriorityQueue;import java.util.Scanner;​public class Main {public static void main(String[] args) {// 创建Scanner对象用于读取输入Scanner sc new Scanner(System.in);// 读取两个整数n和m,分别表示数组的…

【uniapp原生】实时记录接口请求延迟,并生成写入文件到安卓设备

在开发实时数据监控应用时,记录接口请求的延迟对于性能分析和用户体验优化至关重要。本文将基于 UniApp 框架,介绍如何实现一个实时记录接口请求延迟的功能,并深入解析相关代码的实现细节。 前期准备&必要的理解 1. 功能概述 该功能的…

DeepSeek能画流程图吗?分享一种我正在使用的DeepSeek画流程图教程

‍‌​​‌‌​‌​‍‌​​​‌‌​​‍‌​​​‌​‌​‍‌​​‌​​‌​‍‌​‌‌‌‌​​‍‌​‌​‌‌​​‍‌​​​‌‌‌‌‍‌​‌‌​‌‌‌‍‌‌​​‌​‌​‍‌​​‌‌​‌‌‍‌​​​‌​‌​‍‌​‌‌‌​‌‌‍‌‌​​‌‌‌‌‍‌​‌‌‌​​​‍‌…

基于Electron的应用程序安全测试基础 — 提取和分析.asar文件的案例研究

目录: 4.4. 案例研究 4.4.2. 情况描述 4.4.3. 信息收集 4.4.3.2. 检查隐藏目录(点目录)的可能性 4.4.3.3. 使用 DB Browser for SQLite 打开 .db 文件 4.4.3.4. 寻找加密算法 4.4.3.5. 找到加密算法 4.4.3.6. 理解加密流程 4.4.3.7. 找到“Ke…

代码随想录算法训练day64---图论系列8《拓扑排序dijkstra(朴素版)》

代码随想录算法训练 —day64 文章目录 代码随想录算法训练前言一、53. 117. 软件构建—拓扑排序二、47. 参加科学大会---dijkstra(朴素版)总结 前言 今天是算法营的第64天,希望自己能够坚持下来! 今天继续图论part!今…

WPF中对滚动条进行平滑滚动

有时候我们在动态添加内容时,需要将滚动条滚动到指定内容处。 一般我们会调用ScrollViewer的ScrollToVerticalOffset(垂直方向)函数和ScrollToHorizontalOffset(水平方向)函数来控制滚动条滚动到指定位置。 正常滚动效…

Python 课堂点名桌面小程序

一、场景分析 闲来无事,老婆说叫我开发一个课堂点名桌面小程序,给她在课堂随机点名学生问问题。 人生苦短,那就用 Python 给她写一个吧。 二、依赖安装 因为要用到 excel,所以安装两个依赖: pip install openpyxl…

设计模式——过滤器模式在 Spring 中的实践

设计模式——过滤器模式在 Spring 中的实践 基础介绍模块介绍简单实现业务落地额外问题 基础介绍 过滤器模式(Filter Pattern),也称为标准模式(Criteria Pattern),是结构型设计模式之一,旨在通…

Linux网络 数据链路层

在Linux网络中,数据链路层位于物理层之上,网络层之下,其主要职责是将网络层的IP数据包封装成帧,并通过物理链路发送到目标设备。同时,它还负责接收来自物理层的帧,并将其解封装为数据包,传递给网…

Java 调试模式下 Redisson 看门狗失效

一、场景分析 前几天在做分布式锁测试: 在调试模式下,lock.lock() 之后打上断点,想测试一下在当前线程放弃锁之前,别的线程能否获取得到锁。 发现调试模式下,看门狗机制失效了,Redis 上 30 秒后&#xff0…

ktransformers 上的 DeepSeek-R1 671B open-webui

ktransformers 上的 DeepSeek-R1 671B open-webui 一、下载GGUF模型1. 创建目录2. 魔塔下载 DeepSeek-R1-Q4_K_M3. 安装显卡驱动和cuda4. 显卡 NVIDIA GeForce RTX 4090 二、安装ktransformers1. 安装依赖2. 安装uv工具链3. 下载源码4. 创建python虚拟环境 三、编译ktransforme…

线性模型 - 支持向量机

支持向量机(SVM)是一种用于分类(和回归)的监督学习算法,其主要目标是找到一个最佳决策超平面,将数据点分为不同的类别,并且使得分类边界与最近的数据点之间的间隔(margin&#xff09…

html中的元素(2)

在用块级元素完成网页的组织和布局以后&#xff0c;要为其中的每一个小区块添加内容&#xff0c;就需要用到行内元素&#xff1a; 1.字体样式元素 <!DOCTYPE html> <html> <head><meta charset"utf-8"><title>HTML5 保留的文本格式元…

Pytorch中的主要函数

目录 一、torch.manual_seed(seed)二、torch.cuda.manual_seed(seed)三、torch.rand(*size, outNone, dtypeNone, layouttorch.strided, deviceNone, requires_gradFalse)四、给大家写一个常用的自动选择电脑cuda 或者cpu 的小技巧五、torch.version.cuda&#xff1b;torch.bac…

php特性

文章目录 函数特性匹配数组报错进制转换绕过正则表达式匹配换行绝对路径绕过 弱类型语言隐式转换核心概念转换规则 运算符优先级 函数特性 匹配数组报错 以此为例&#xff0c;如果传入参数是一个数组&#xff0c;则preg_match()函数报错返回0&#xff0c;完成绕过&#xff0c;…