1.异常捕获
(0)导言
程序在运行过程中是有出现错误的可能的,而通常情况下程序执行过程一旦出现错误就会中断。
go语言中为了提高程序的可执行性,提出了异常捕获的机制。
异常捕获允许用户通过特殊的写法,当程序运行出现异常时,不去执行错误的代码,
而是转而执行一句没有问题的替代代码,从而避免程序中断。
在go语言中提供了四种异常捕获的手段:errors接口、panic中断、defer延迟调用和recover拦截
(1)errors接口捕获错误信息
在go语言中错误可以分成三大类,分别为【编辑时异常】【编译时异常】【运行时异常】。
1)编辑时异常:代码编写时的硬语法错误,例如少写逗号或者关键词丢失
num int = 100
//丢失var关键字,程序根本不能执行
//错误通常为syntax error前缀,后接错误信息
2)编译时异常:IDE将代码转换为可执行文件时由于逻辑问题导致的错误,例如除数为0
var num int = 10/0;
//除数为0,但是程序从语法上来讲没错误
//错误通常只有单一的错误信息,division by zero
3)运行时异常:程序在运行后出现的错误,例如数组下标溢出
var arr [3]int = [3]int{1,2,3};
for i:=0; i<4; i++{
fmt.Println(arr[i]);
}
//虽然程序从语法结构上来讲没有任何错误
//而且也不存在例如除数为0,下标是负数这种逻辑上的硬错误
//但是程序在执行到i=3的时候就会引发数组下标越界的问题。
//其错误通常使用runtime error前缀,后接错误信息
而go语言提供了一个名为errors的系统包,它专门用来处理当错误发生时的一些问题
可以通过将errors提供的方法放在可能发生异常问题的地方,来避免程序由于出现异常而中断
err := errors.New("错误信息");
eg:
var arr [3]int = [3]int{1,2,3};
for i:=0; i<4; i++{
if(i>2){
err := errors.New("数组下标访问越界了!");
fmt.Println(err);
break;
}
fmt.Println(arr[i]);
}
ps:其实errors包提供的方法也只是一个提示信息,就和正常的输出语句没什么区别。
仍旧需要自己编写逻辑支持,而不能说单独一个方法就能规避异常产生了。
(2)panic异常处理
当程序出现会发生崩溃中断的异常时,系统会自动调用panic函数来对异常信息进行报告。
与errors不同的是errors一般用来报告一些一般性质的错误,这些错误不一定会引发程序崩溃,
而panic则是用来报告当程序遇到不可恢复的错误状态时的错误信息,这些错误一定会引发程序崩溃。
panic(v {}interface);
eg:
fmt.Println("hello1")
panic("hello2")//程序会在此中断,并打印panic:hello2
fmt.Println("hello3")
ps:通常来讲我们不应该使用panic函数来报告一般性质的普通错误
因为panic函数的调用会直接出发panic异常报告,从而强制导致程序崩溃中断
ps:panic接收任何类型的值作为参数。
(3)defer延迟调用
defer是一个go语言中的关键字,它能够使得defer后的函数调用语句延迟执行,
具体时机是当任务队列中的语句全部执行完毕后。
eg:
defer 函数调用
defer fmt.Println("111");
fmt.Println("222");
fmt.Println("333");
//输出结果顺序是:222 333 111
它的作用类似于JavaScript中的window.setTimeout()延迟调用函数的作用,
但两者并不完全相同:
1)在JavaScript中延迟调用函数是通过系统loop(堆区)完成的。
(堆区特征:先进先出)
在JavaScript中的延迟调用函数在多个函数具有相同延迟时间时,
哪个延迟函数语句先被绑定,哪个延迟函数语句就先执行。
window.setTimeout(function (){console.log(111);},1000};
window.setTimeout(function (){console.log(222);},1000};
//两个函数都等待一秒后执行
//输出结果一定是:111 222
2)而在go语言中的defer延迟调用时通过栈区来完成的。
(栈区特征:后进先出)
在go语言中如果存在多个被defer就是的函数语句,那么哪个函数最后被defer修饰
哪个函数就最先被执行
defer fmt.Println("111");
defer fmt.Println("222");
//两个函数都延迟调用
//输出结果一定是:222 111
当函数中存在返回值时,要慎用defer。因为defer的存在可能会导致返回值赋值落后。
var value int
func Sum (num1,num2 int){
value = num1+num2
}
func main(){
defer Sum(10,20);
fmt.Println(value);//0
}
(4)recover接口拦截错误
运行时panic异常一旦被引发就会导致程序崩溃,这当然不是我们愿意看到的。
因为谁也不能保证程序不会发生任何运行时错误。
Go语言为我们提供了专门用于“拦截”运行时panic的系统函数recover。
它可以使当前的程序从运行时panic的状态中恢复并且重新获得流程控制权。
相关说明:
1)recover函数的作用是阻止panic函数运行,防止程序崩溃。
2)recover函数只有在defer调用的函数中有效。
3)recover函数具有返回值,返回值是捕获到的异常信息,默认值是nil
4)recover函数必须在异常发生之前被声明,尽管由于defer的原因会最后执行
5)recover函数只能检测到第一个发生的panic异常,并将其后的所有内容忽略
6)recover函数的作用范围是函数范围,也就是说超过recover函数范围的部分不受忽略影响
相关语法:
func recover() interface{}
具体案例:
func proRecover (){
//recover函数的常规使用方式
//尽管defer的存在使得本AIIFE会最后执行
//但是这个“声明”必须写在可能发生的异常之前
defer func() {
result := recover();
if result != nil{
fmt.Println(result);
}
}()
//第一个异常:空指针
var p *int;
*p = 100;
//第二个异常:数组下标越界
arr := [3]int{1,2,3};
idx := 100;
arr[idx] = 100;
}
func main() {
//程序执行,检测到空指针异常
//并且proRecover函数内第一个异常后的所有代码都没能继续执行。
proRecover();
//但是超过了recover所在的函数范围的内容则不会受到影响
//换句话说下面这句打印111会正常输出
fmt.Println(111);
}
2.文件操作
(0)导言
go语言的文件操作沿袭了传统c语言对文件操作的规定,都是需要通过文件指针来读写操作文件。
但不同之处在于go语言不需要创建File类型指针,而是通过OS系统包中提供的函数来完成对文件的操作。
(1)创建文件
文件指针,err标识 := os.Create("文件路径");
fp,err := os.Create("./test.txt");
ps:文件路径通常情况下都通过相对路径来创建,绝对路径可能会出现一些不必要的问题。
ps:文件创建时如果不存在会创建新文件,如果文件存在会将原文件覆盖。
(2)打开文件
1)os.Open("文件路径")
本方法默认以只读的权限打开文件,打开后不能修改文件内容。
本方法不能创建新文件,即如果文件不存在,是会打开失败的。
下面的os.OpenFile方法同理。
fp,err := os.Open("./test.txt");
defer fp.Close();
if err!=nil{
fmt.Println("文件打开失败");
return;
}
fmt.Println("文件打开成功")
length,err := fp.WriteString("hello\n大家好");
if err!=nil {
fmt.Println("文件写入失败");
return;
}
fmt.Println(length);
//输出结果显然是打开成功,写入失败
2)os.OpenFile("文件路径",打开模式,操作权限)
本方法打开文件的模式是根据参数来决定的,其中
·打开模式: os._RDONLY(只读模式)
os._WRONLY(只写模式)
os._RDWR(读写模式)
os._APPEND(追加模式)
·操作权限: 0 没有任何权限
1 执行权限(如果文件是可执行文件,那么有权执行文件)
2 写权限
3 写权限和执行权限
4 读权限
5 读权限和执行权限
6 读权限和写权限
7 读写权限和执行权限
fp,err := os.OpenFile("./test.txt",os.O_RDWR,7)
defer fp.Close();
if err!=nil{
fmt.Println("文件打开失败");
return;
}
fmt.Println("文件打开成功")
length,err := fp.WriteString("hello\n大家好");
if err!=nil {
fmt.Println("文件写入失败");
return;
}
fmt.Println(length);
//此时输出结果为:文件打开成功 15
(3)开闭原则
文件打开(创建后默认打开)后必须手动进行关闭操作,通过文件指针打开的文件不会自动关闭。
如果不主动进行关闭,会大量占用系统的内存memory资源与缓冲区buffer资源
fp,err := os.Create("./test.txt");
if err!=nil{
fmt.Println("文件创建失败");
return
}
defer fp.Close();
fmt.Println("文件创建成功");
ps:可以使用defer延迟调用,保证文件关闭操作总是在最后执行。避免影响文件的其他读写操作
ps:一个程序能够打开的文件数量是有限的,如果超过65535个文件会产生不可预知的后果。
(4)写入文件
1)相关说明
·文件写入必须在文件创建成功或打开成功之后进行,文件打开之后必须被正确关闭!!
·在windows换行是\n\r, unix和linux是\n
·go语言中中文占3个字符长度
2)写入方法
所有的写入方法如果不是从文件的末尾开始写入内容,都会从写入的位置开始覆盖源内容。
·fp.WriteString("string")
length,err := fp.WriteString("写入内容");//length是写入字符串的长度
eg:
length,err := fp.WriteString("hello\n大家好");
if err!=nil {
fmt.Println("文件写入失败");
return;
}
fmt.Println(length);//15
·fp.Write([]byte)
length,err := fp.Write([]byte{});//length是写入字符串的长度
eg:
tempStr := "你好我是一个雇佣兵,我就要上前线了";
tempSlice := []byte(tempStr);//字符串强转字符切片
length,err := fp.Write(tempSlice);
fmt.Println(length);
·fp.WriteAt()
length,err := fp.WriteAt([]byte, int64);//idx是开始写入文件内容的光标位置
eg:
tempStr := "你好我是一个雇佣兵,我就要上前线了";
tempSlice := []byte(tempStr);//字符串强转字符切片
length,err := fp.WriteAt(tempSlice, 0);
fmt.Println(length);
3)光标偏移
写入文件时如果需要主动调整写入的位置,可以通过seek方法来进行“光标”的调整
n,err := fp.seek(offset:偏移量,whence从哪个位置计算偏移量);
whence是一个int类型的参数:
io_SeekCurrent --之前是--> os.SEEK_CUR 当前位置
io_SeekEnd --之前是--> os.SEEK_END 结束位置
io_SeekStart --之前是--> os.SEEK_SET 起始位置
n为最后得到的光标所在位置
ps:
如果当前光标已经在末尾,而offset偏移量又继续向后偏移
那么最终写入文件的时候,实际内容紧挨着前文,但是文件整体长度会发生变化。
已知文件内容是abcde (5字节大小)
然后执行下列代码
n,_ := fp.seek(10, io,SeekEnd)
length,_ := fp.WriteAt([]byte{'-'},n)
fmt.Println(n,length)
那么最终控制台中输出结果是:15 1
而文件内容会变更为abcde- (16字节大小)
(5)读取文件
1)相关说明
·文件读取必须在文件创建成功或打开成功之后进行,文件打开之后必须被正确关闭!!
2)读取方法
所有的读取方法都是默认读取到文件结束为止。如果存储数据的容器有大小限制,
那么读取方法就会按照容器的大小限制读取,直到将容器存满为止。
·fp.Read(容器)
已知文件内容是abcde
然后执行下列代码
data := make([]byte, 100);
fp.Read(data);
那么最终控制台中输出结果是:abcde
·fp.readAt(容器,光标位置)
已知文件内容是abcde
然后执行下列代码
data := make([]byte, 100);
n,_ := fp.Seek(1,io.SeekStart);
fp.ReadAt(data,n);
那么最终控制台中输出结果是:bcde
·缓冲区读取
除了直接通过文件进行读取之外,还能够使用系统提供的buffer缓冲区进行读取。
因为buffer缓冲区提供了很多读取文件的方法。
已知文件内容是:abcde
12345
然后执行下列代码
buffer := bufio.NewReader(fp)
for{
buf,err := buffer.ReadBytes('\n');//遇到\n就停止当前次读取的内容,但是当前这个字符也能读取
if err!=nil{
if err == io.EOF{//文件末尾
break
}
}
fmt.Printf("%s",string(buf));
}
那么最终控制台中输出结果是:abcde
12345

本文详细介绍了Go语言中的异常捕获机制,包括errors接口、panic、defer和recover的使用。同时,讲解了文件操作的相关内容,如创建、打开、读写文件的方法及其注意事项,强调了文件管理和异常处理的重要性。
1249

被折叠的 条评论
为什么被折叠?



