Go语言入门——进阶语法篇(四)

异常处理与文件

异常处理

error 处理

Go语言没有类似Java或Python那种try...catch...机制处理异常,Go的哲学是与众不同的,Go的设计者认为主流的异常处理机制是一种被过度滥用的技巧,而且存在很大的潜在危害,Go的异常处理(或者说是错误处理)是一种非常简单直观的方式。通常的,我们在写Java、Python之类的代码时,遇到可能存在的异常,直接用try括起来,使用catch捕获,然后就万事大吉了,当系统长时间的运行时,大大增加了不稳定性,所积累的问题可能在某一刻爆发。而Go者使用一种称为"恐慌的"机制,在有必要时,直接让系统宕机,让问题发生时立刻暴露出来,不必累积。很难说哪种设计更好,但Go语言确实简化了代码。

Go语言引入了一个错误处理的标准接口:error接口,并习惯性的默认将错误作为最后一个返回值,当然,如果有的话。如果我们要自定义错误类型,实现该接口即可

type error interface {
    Error() string
}

错误处理示例

type MyError struct {
	content   string
	errorCode int
}

// 让MyError实现error接口
func (this *MyError) Error() string {
	return fmt.Sprintf("%d:%s",this.errorCode,this.content)
}

func div(a, b int) (r int, err error) {
	var e *MyError = new(MyError)
	if b == 0 {
		e.errorCode = 101
		e.content = "除数不能为0"
		return 0, e
	}
	return a / b, nil
}

func main() {
	r, e := div(1, 0)
	if e != nil {
		// 错误不为空,打印错误
		fmt.Println(e)
		return
	}
	fmt.Println(r)
}

打印结果:

101:除数不能为0

自定义类型实现error接口可以提供更丰富的错误信息,但有时候我们希望快速的生成一个简单的错误,而不是写个结构体,那么Go还提供了一种快捷创建错误的方式,使用errors

package main

import (
	"fmt"
	"errors"
)

var errByZero = errors.New("除数不能为0")

func div(a, b int) (r int, err error) {
	if b == 0 {
		return 0, errByZero
	}
	return a / b, nil
}

注意,为了提升性能,errors.New方法不建议在函数中调用,错误的内容是不会变的,可以在函数外声明好需要的错误,就如同声明一些常量一样。该方法虽然简单,但是包含的错误信息有限,酌情使用。

panic 与 recover

panic词义为恐慌,recover则表示恢复。

仍以除数是0为例

func div(a, b int) int {
	if b == 0 {
		panic("crash:除数为0")
	}
	return a / b
}

func main() {
	r := div(1, 0)
	fmt.Println(r)
}

运行代码后,程序直接奔溃,并输出了调用信息

panic: crash:除数为0

goroutine 1 [running]:
main.div(...)
        C:/Users/ysk/Desktop/hello.go:12
main.main()
        C:/Users/ysk/Desktop/hello.go:18 +0x41
exit status 2

这样,当我在开发和调试时,出现问题,通过手动调用panic让程序崩溃,及时发现并解决问题,包括生成环境中的测试,而不是等到系统上线运行一段时候之后才发现问题。

有时候,我们可能很害怕奔溃,recover则可以在这种奔溃发生时,恢复程序,使得程序可以继续运行。简单说,panicrecover的组合,可以模拟实现Java中的try...catch机制,将异常捕获,而不是继续向上传递。但这并不是Go语言所推崇的用法。

func main() {
    // 加上一段代码,defer后面跟一个匿名函数,匿名函数中使用recover捕获到错误
	defer func(){
		err := recover()
		fmt.Printf("处理:%s\n",err)
	}()

	r := div(1, 0)
	fmt.Println(r)
}

打印结果:

处理:crash:除数为0

可以看到,使用recover处理后,程序不再奔溃了。

延迟处理

上面示例出现了一个关键字defer,该关键字就是用于延迟处理。我们上面说了Java中的trycatch,那怎么能没有finally呢。defer其实就相当于finally,在整个函数调用完后,最后执行一些关闭句柄的功能。Go中,defer除了关闭句柄,还可用于释放并发锁。

func main() {
	defer fmt.Println("这是defer调用")
	fmt.Println("Hello,world!")
	fmt.Println("Hello,go!")
}

打印结果:

Hello,world!
Hello,go!
这是defer调用

可以看到,defer语句写在最先,但是却在最后才被执行。同一个函数中是可以使用多个defer语句的,多个defer语句的执行顺序遵循栈结构特点,先进后出,最先的defer语句最后执行

func main() {
	defer fmt.Println("这是defer语句1")
	defer fmt.Println("这是defer语句2")
	
	fmt.Println("Hello,world!")
	defer fmt.Println("这是defer调用3")
	fmt.Println("Hello,go!")
	defer fmt.Println("这是defer调用4")
}

打印结果:

Hello,world!
Hello,go!
这是defer调用4
这是defer调用3
这是defer语句2
这是defer语句1

文件读写

带缓冲区

package main

import (
	"fmt"
	"os"
	"io"
	"bufio"  // 导入缓冲包
)

func main() {
	// 1. 创建文件
	file , err := os.Create("D:/test.txt")
    if err != nil{
        fmt.Println("创建文件失败")
        return
    }
    // 2. 关闭文件
    defer file.Close()

    // 3. 创建写入缓冲
	 w := bufio.NewWriter(file)
	// 4. 写入字符串
    len, err := w.WriteString("hello world")
    if err != nil {
      fmt.Println("写入失败")
    }else{
     fmt.Printf("写入了%d个字符\n",len)
    }  
    // 4. 刷新缓冲,写入硬盘
	w.Flush()
	
	// -------------------分割线---------------------

	// 1. 打开文件
    rfile, e := os.Open("D:/test.txt")
    if e != nil{
        fmt.Println("打开文件失败")
        return
    }
    // 2.关闭文件
	defer rfile.Close()
	
    // 3. 创建读取缓冲
	r := bufio.NewReader(rfile)
	
	// 4. 读取文件。使用带缓冲的方式读取文件,只能一行一行的读取
	// 该方式适用于高效的读写大文件
	for {
		str,err := r.ReadString('\n') 
		fmt.Println(str)

		if err == io.EOF {  // io.EOF表示文件的末尾
			break
		}
	}
}

小结

  1. 使用os.Create创建文件会覆盖掉已存在的文件
  2. 缓冲区默认大小为4096,可以使用NewReaderSizeNewWriterSize在创建缓冲时指定大小
  3. 如需以二进制方式读写文件,将WriteStringReadString换成WriteByte(c byte)ReadByte(),亦可使用Write(p []byte)Read(p []byte)方法。区别是WriteByteReadByte每次读写一个字节,WriteRead每次读写一个切片的字节。

使用 ioutil

在操作小文件时,可以不指定缓冲区,那么就可以使用一种更简单的方式读写文件。

package main

import (
	"fmt"
	"io/ioutil"
)

func main() {
	// 1. 字符串转字节切片
	buf := []byte("golang write string")
	// 2. 将字节切片直接写入文件,文件不存在则创建,存在则覆盖
	err := ioutil.WriteFile("D:/io_test.txt", buf, 0666)
	if err != nil {
		fmt.Println("写入失败")
	} else {
		fmt.Println("写入成功")
	}

	// -------------------分割线---------------------

	// 1. 读取指定文件,返回字节切片
	data, e := ioutil.ReadFile("D:/io_test.txt")
	if e != nil {
		fmt.Println(e)
	} else {
		// 2. 将字节切片转字符串输出
		fmt.Println(string(data))
	}
}

小结

  1. 使用ioutil不需要手动打开和关闭文件,打开和关闭操作已被封装了
  2. 使用ioutil.WriteFile仍然存在覆盖已有文件的问题,如需对文件进行追加操作,应使用其他方式
  3. WriteFileReadFile是以字节的方式操作文件,如需处理文本文件,应手动转换字节与字符串

文件追加

很多时候我们不希望新文件覆盖旧文件,而是在旧文件中继续添加内容。这时候必须使用指定模式的方式来打开文件。

package main

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

func main() {
	// 1. 打开文件,指定文件操作模式: 读写追加
	file,err := os.OpenFile("D:/test.txt",os.O_RDWR|os.O_APPEND,0666)
	if err != nil{
        fmt.Println("打开文件失败")
        return
	}
	
	// 2. 函数执行结束关闭文件
	defer file.Close()

	// 3. 创建写缓冲
	writer := bufio.NewWriter(file)
	writer.WriteString("text content")

	// 4. 刷新缓冲
	writer.Flush()
}

常用组合

模式组合说明
O_RDONLY只读模式
O_WRONLY只写模式
O_RDWR可读可写模式
os.O_WRONLY|os.O_CREATE写|创建
os.O_WRONLY|os.O_TRUNC写|覆盖
O_WRONLY|os.O_APPEND写|追加
os.O_RDWR|os.O_APPEND读写|追加

小结

  • os.OpenFile函数的最后一个参数表示Unix系统中的文件权限,在Windows系统上被忽略。

文件的其他操作

获取文件信息
package main

import (
	"fmt"
	"os"
)

func main() {
	// 获取文件信息
	fileInfo, err := os.Stat("D:/test.txt")
	if err != nil{
        fmt.Println("打开文件失败")
        return
	}
	fmt.Println("文件名:", fileInfo.Name())
	fmt.Println("文件大小:", fileInfo.Size())
	fmt.Println("文件权限:", fileInfo.Mode())
	fmt.Println("最后修改时间:", fileInfo.ModTime())
	fmt.Println("是否是文件夹:", fileInfo.IsDir())
	fmt.Printf("系统信息:%+v\n", fileInfo.Sys())
}
判断文件是否存在
package main

import (
	"fmt"
	"os"
)

// 定义一个函数,判断文件是否存在
func exists(path string) (bool, error) {
    _, err := os.Stat(path)
    if err == nil {
        return true, nil
    }
    if os.IsNotExist(err) {
        return false, nil
    }
    return false, err
}

func main() {
	if b,_ := exists("D:/test.txt");b {
		fmt.Println("文件存在")
	}else{
		fmt.Println("文件不存在")
	}
}
文件拷贝
package main

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

func main() {
	var dst string = "D:/files/test.txt" // 目标路径
    var src string = "D:/test.txt"       // 源路径

    srcFile ,err := os.Open(src)
    if err != nil {
        fmt.Println("打开失败:",err)
        return 
    }
    defer srcFile.Close()
    reader := bufio.NewReader(srcFile)


    dstFile,err := os.OpenFile(dst,os.O_WRONLY|os.O_CREATE,0666)
    if err != nil {
        fmt.Println("打开失败:",err)
        return 
    }
    defer dstFile.Close()

	writer := bufio.NewWriter(dstFile)
	
	// 使用Coyp函数完成文件拷贝
    if _, err = io.Copy(writer, reader); err != nil {
        fmt.Println("拷贝失败:",err)
        return
    }
    fmt.Println("拷贝成功!")
}
移动、删除和重命名
package main

import (
	"fmt"
	"os"
)

func main() {
	src ,dst := "D:/test.txt","D:/workspace/test.txt"

	// 文件移动(Rename既可重命名也可移动文件)
	err := os.Rename(src, dst)
    if err != nil {
        fmt.Println("移动失败:",err)
        return 
    }
	// 文件删除
	err = os.Remove("D:/io_test.txt")
	if err != nil {
        fmt.Println("删除失败:",err)
        return 
    }
    // 文件重命名
	oldName, newName := "D:/logcat.txt", "D:/log.txt"
    err = os.Rename(oldName, newName)
    if err != nil {
        fmt.Println("重命名失败:",err)
        return 
    }
}

欢迎关注我的公众号:编程之路从0到1

编程之路从0到1

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
应用背景为变电站电力巡检,基于YOLO v4算法模型对常见电力巡检目标进行检测,并充分利用Ascend310提供的DVPP等硬件支持能力来完成流媒体的传输、处理等任务,并对系统性能做出一定的优化。.zip深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程之路从0到1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值