Golang学习笔记(3)

本文介绍了Go语言中的函数使用,包括自定义函数、系统函数、函数定义、调用机制、递归调用以及错误处理。此外,还详细讲解了包的概念、管理和使用,包括包的定义、导入、作用域以及初始化函数。同时,文中提到了匿名函数、闭包、defer机制以及函数参数的传递方式。最后,文章探讨了Go中的错误处理,包括panic、recover和自定义错误的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第六章 函数

函数介绍

为完成某一个功能的程序指令(语句)的集合,称为函数
在Go中,函数分为:自定义函数、系统函数。

函数的定义

  • 基本语法
    func 函数名(形参列表)(返回值列表){
    执行语句…
    return返回值列表
    }
  1. 形参列表:表示函数的输入
  2. 函数中的语句:表示为了实现某一功能的代码块
  3. 函数可以与返回值,也可以没有
  • 案例分析
package main

import (
	"fmt"
)

// 将计算的功能,放入到一个函数中,然后在需要使用时,调用即可
func cal(n1 float64, n2 float64, operator byte) float64 {
	var res float64
	switch operator {
	case '+':
		res = n1 + n2
	case '-':
		res = n1 - n2
	case '*':
		res = n1 * n2
	case '/':
		res = n1 + n2
	default:
		fmt.Println("操作符号错误")
	}
	return res
}
func main() {
	var n1 float64 = 1.2
	var n2 float64 = 2.3
	var operator byte = '-'
	res := cal(n1, n2, operator)
	fmt.Println("res= ", res)
	n1 = 7.9
	n2 = 6.0
	operator = '*'
	res = cal(n1, n2, operator)
	fmt.Println("res=", res)
}

包的介绍

  • 包的原理:报的本质实际上就是创建不同的文件夹,来存放程序文件。
  • 包的基本概念
  • 说明:go的每一个文件都属于一个包,go是以包的形式来管理文件和项目目录结构的
  • 包的三大作用
  1. 区分相同名字的函数、变量等标识符
  2. 当程序文件很多时,可以很好的管理项目
  3. 控制函数、变量等访问范围,即作用域
  • 包的使用
  1. 打包基本语法
    package 包名
  2. 引入包的基本语法
    import"包的路径"
    案例演示
    在这里插入图片描述
    utils.go
package utils

import (
	"fmt"
)
//为了让其他文件使用Cal函数,需要将C大写,类似于public
func Cal(n1 float64, n2 float64, operator byte) float64 {
	var res float64
	switch operator {
	case '+':
		res = n1 + n2
	case '-':
		res = n1 - n2
	case '*':
		res = n1 * n2
	case '/':
		res = n1 + n2
	default:
		fmt.Println("操作符号错误")
	}
	return res
}

main.go

package main

import (
	"fmt"
	"go_code/chapter06/fundemo01/utils"//引入utils中的函数
)

func main() {
	var n1 float64 = 1.2
	var n2 float64 = 2.3
	var operator byte = '-'
	res := utils.Cal(n1, n2, operator)//调用函数
	fmt.Println("res= ", res)
	n1 = 7.9
	n2 = 6.0
	operator = '*'
	res = utils.Cal(n1, n2, operator)//调用函数
	fmt.Println("res=", res)
}
  • 包的注意事项和使用细节
  1. 在给一个文件打包时,该包对应一个文件夹,比如utils文件夹对应的就是utils,文件的包名通常和文件所在的文件夹名一致,一般用小写字母。
  2. 当一个文件要使用其它包的函数或者变量时,应该先引入对应的包
    1)package指令在文件第一行,然后是import指令
    2)在import宝石,路径从$GOPATH的src下开始,不用带src
  3. 为了让其它包的文件,可以访问到本宝的函数,则该函数名的首字母需要大写,类似其它语言中的public,这样才能挎包访问。
func Cal(n1 float64, n2 float64, operator byte) float64 {
}
  1. 在访问其它包函数是变量时,其语法是包名.函数名。比如:
utils.Cal(n1,n2,"+")
  1. 如果包名较长,go语言支持给包取别名,注意:取别名后,原来的包名不能使用
import (
	"fmt"
	util "go_code/chapter06/fundemo01/utils"//引入utils中的函数
)

如果给包取了别名,则需要使用别名来访问该包的函数和变量。

  1. 在同一个包下,不能有相同的函数名,否则报重复定义。
  2. 如果要编译成一个可执行程序文件,就需要将这个包声明为main,即package main.这个就是一个语法规范,如果是写一个库,包名可以自定义
    1)在$ GOPATH的目录下进行编译
    2)编译时需要编译main包所在的文件夹
    3)编译的目录结构最好按照规范来组织
    4)编译后生成一个有默认名的可执行文件,在$GOPATH目录下,可以指定名字和目录,比如:将可执行文件名称改为my.exe放在bin目录下:D:\go_test>go build -o bin/my.exe go_code/project/main

函数-调用机制

  • 函数的调用过程
    1)传入一个数+1
package main

import "fmt"

// 编写一个函数test
func test(n1 int) {
	n1 = n1 + 1
	fmt.Println("test() n1=", n1)
}
func main() {
	n1 := 10
	//调用test
	test(n1)
	fmt.Println("main() n1=", n1)
}

在这里插入图片描述
说明:
(1)在调用一个函数是,会给该函数分配一个新的空间,编译器会通过自身的处理让这个新的空间和其它栈的空间进行区分
(2)在每个函数对应的栈中,数据空间是独立的,不会混淆在一起
(3)当一个函数调用完毕后(执行完毕后),程序会回收(销毁)函数对应的栈空间
2)计算两个数相加,返回getSum

package main

import "fmt"
func getSum(n1 int, n2 int) int {
	sum := n1 + n2
	return sum
}
func main() {
	n1 := 10
	n2 := 20
	sum := getSum(n1, n2)
	fmt.Println("main sum =", sum)
}
  • return语句
    基本语法:
    Go函数支持返回多个值
    func 函数名(形参列表)(返回值类型列表){
    语句…
    return返回值列表
    }
  1. 如果返回多个值,在接收时,希望忽略某个函数值,使用_符号表示占位忽略
  2. 如果返回值只有一个,(返回值类型列表)可以不写()
func getSumAndSub(n1 int, n2 int) (int, int) {
	sum := n1 + n2
	sub := n1 - n2
	return sum, sub
}
func main() {
	n1 := 10
	n2 := 20
	sum, sub := getSumAndSub(n1, n2)

	fmt.Printf("main sum = %v main sub = %v", sum, sub)

}

函数-递归调用

  • 基本介绍:
    一个函数在函数体内又调用了本身,称为递归调用
  • 快速入门
package main

import "fmt"

func test(n int) {
	if n > 2 {
		n--
		test(n)
	}
	fmt.Println("n=", n)
}
func main() {
	test(4)

}
package main

import "fmt"
func test2(n int) {
	if n > 2 {
		n--
		test2(n)
	} else {
		fmt.Println("n=", n)
	}
}
func main() {
	test2(4)

}
  • 函数递归需要遵守的重要原则:
  1. 执行一个函数时,就创建一个新的受保护的独立空间(新函数栈)
  2. 函数的局部变量是独立的,不会相互影响
  3. 递归必须向退出递归的条件逼近,否则就是无限递归
  4. 当一个函数执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,当函数执行完毕或者返回时,该函数本身就会被编译器销毁。
  • 案例演示
  1. 使用递归的方法,输入一个整数,求出对应的斐波那契数
package main

import "fmt"

// 使用递归的方法,输入一个整数,求出对应的斐波那契数
func fbn(n int) int {
	if n == 1 || n == 2 {
		return 1
	} else {
		return fbn(n-1) + fbn(n-2)
	}
}
func main() {
	var a int
	fmt.Println("请输入一个整数= ")
	fmt.Scanln(&a)
	res := fbn(a)
	fmt.Println("res =", res)
}

  1. 求函数值,已知f(1)=3;f(n)=2*f(n-1)+1;使用递归的思想编程,求出f(n)的值
package main

import "fmt"

// 求函数值,已知f(1)=3;f(n)=2*f(n-1)+1;使用递归的思想编程,求出f(n)的值
func f(n int) int {
	if n == 1 {
		return 3
	} else {
		return 2*f(n-1) + 1
	}
}
func main() {
	var num int
	fmt.Println("请输入一个整数= ")
	fmt.Scanln(&num)
	res := f(num)
	fmt.Println("f(n)=", res)
}
  1. 题3:猴子吃桃子问题
    有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个!以后每天猴子都吃
    其中的一半,然后再多吃一个。当到第十天时,想再吃时(还没吃),发现只有
    1个桃子了。问题:最初共多少个桃子?
package main

import "fmt"

// 题3:猴子吃桃子问题
// 有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个!以后每天猴子都吃
// 其中的一半,然后再多吃一个。当到第十天时,想再吃时(还没吃),发现只有
// 1个桃子了。问题:最初共多少个桃子?
func peach(n int) int {
	if n > 10 || n < 1 {
		fmt.Println("输入的天数错误")
		return 0
	} else if n == 10 {
		return 1
	} else {
		return (peach(n+1) + 1) * 2
	}
}
func main() {
	fmt.Println("第一天的桃子数量是=", peach(1))
}

函数注意事项和细节

  1. 函数的形参列表可以是多个,返回值列表也可以是多个
  2. 形参列表和返回值列表的数据类型可以是值类型和引用类型
  3. 函数的命名遵循标识符命名规范,首字母不能是数字,首字母大写该函数可以被本包文件和其它包文件使用,首字母小写,只能被本报文件使用,其它包文件不能使用
  4. 函数中的变量是局部的,函数外不生效
  5. 基本数据类型和数组默认都是值传递的,即进行值拷贝。在函数内修改,不会影响到原来的值
  6. 如果希望函数内的变来给你能修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量。从效果上类似引用。
  7. Go函数不支持重载
    在这里插入图片描述
  8. 在go中,函数也是一种数据类型,可以赋值给一个变量,则该变量就是一个函数类型的变量了。通过该变量可以对函数调用。
package main

import "fmt"

// 在go中,函数也是一种数据类型,
// 可以赋值给一个变量,则该变量就是一个函数类型的变量了。通过该变量可以对函数调用。
func getSum(n1 int, n2 int) int {
	return n1 + n2
}
func main() {
	a := getSum
	fmt.Printf("a的类型%T,getSum类型是%T\n", a, getSum)
	res := a(10, 40) //等价于res:=getSum(10,40)
	fmt.Println("res=", res)
}

  1. 函数既然是一种数据类型,因此在Go中,函数可以作为形参,并且调用。
package main

import "fmt"

// 在go中,函数也是一种数据类型,
// 可以赋值给一个变量,则该变量就是一个函数类型的变量了。通过该变量可以对函数调用。
func getSum(n1 int, n2 int) int {
	return n1 + n2
}

//函数既然是一种数据类型,因此在Go中,函数可以作为形参,并且调用。
func myFun(funvar func(int, int) int, num1 int, num2 int) int {
	return funvar(num1, num2)
}
func main() {
	a := getSum
	fmt.Printf("a的类型%T,getSum类型是%T\n", a, getSum)
	res := a(10, 40) //等价于res:=getSum(10,40)
	fmt.Println("res=", res)

	res2 := myFun(getSum, 50, 60)
	fmt.Println("res2=", res2)
}

  1. 为了减缓数据类型定义,Go支持自定义数据类型
    基本语法:type 自定义数据类型名 数据类型 //理解:相当于一个别名
    案例:type myInt int //myInt等价于int使用(注意:go认为myInt和int是两个类型)
    案例:type mySum func(int,int)int // mySum等价于func(int,int)int使用
  2. 支持对函数返回值命名
func cal(n1 int ,n2 int)(sum int ,sub int){
	sum = n1 + n2
	sub = n1 - n2
	return
}
  1. 使用_标识符,忽略返回值
func cal(n1 int, n2 int )(sum int, sub ing){
	sum = n1 + n2
	sub = n1 - n2
	return
}
func main(){
	res1,_:=cal(10,20)
	fmt.Printf("res1=%d",res1)
}
  1. Go支持可变参数
    //支持0到多个参数
    func sum(args…int)sum int{
    }
    //支持1 到多个参数
    func sum(n1 int,args…int)sum int{
    }
    说明;
    (1)args是slice 切片,通过args[index]可以访问到各个值
    (2)如果一个函数的形参列表中有可变参数,则可变参数需要放在参数列表最后。

init函数

基本介绍

每一个源文件都可以包含一个init函数(初始化),该函数在main函数执行前,被Go运行框架调用,也就是说init会咋main函数前被调用。

案例说明

package main

import "fmt"

// init函数,通常可以在init函数中完成初始化工作
func init() {
	fmt.Println("init()...")
}
func main() {
	fmt.Println("main()...")
}

输出结果
在这里插入图片描述

init函数的注意事项和细节

  1. 如果一个文件中同时包含全局变量定义,init函数和main函数,则执行的流程是变量定义>>init函数>>main函数
package main

import "fmt"

var age = test()

// 为了看到全局变量是先被初始化的,我们这先写一个函数完成初始化
func test() int {
	fmt.Println("test()")
	return 90
}

// init函数,通常可以在init函数中完成初始化工作
func init() {
	fmt.Println("init()...")
}
func main() {
	fmt.Println("main()...age=", age)
}

在这里插入图片描述

  1. init函数最主要的作用,是完成一些初始化的工作。
    utils.go
package utils

import "fmt"

var Age int
var Name string

// Age和Name在main.go中使用
// 但是需要初始化Age和Name
// 使用init函数完成初始化函数
func init() {
	fmt.Println("utils")
	Age = 100
	Name = "Tom"
}

main.go

package main

import (
	"fmt"
	"go_code/chapter06/functestinit/utils"
)

var age = test()

// 为了看到全局变量是先被初始化的,我们这先写一个函数完成初始化
func test() int {
	fmt.Println("test()")
	return 90
}

// init函数,通常可以在init函数中完成初始化工作
func init() {
	fmt.Println("init()...")
}
func main() {
	fmt.Println("main()...age=", age)
	fmt.Println("Age = ", utils.Age, "Name= ", utils.Name)
}

输出结果
在这里插入图片描述
3. 面试题:案例如果main.go和utils.go都含有变量定义,init函数时,执行流程是什么?
utils和main.go都含有变量定义时,先执行被引入包中的变量定义,后执行iutils中nit函数,在执行main.go中的变量定义>>init函数>>main函数
在这里插入图片描述

匿名函数

介绍

Go支持匿名函数,如果某个函数只是希望使用一次,可以考虑使用匿名函数,匿名函数也可以实现多次调用。

匿名函数使用方法1

在定义匿名函数时直接调用(这种方式匿名函数只能调用一次)

res1 := func(n1 int, n2 int) int {
		return n1 + n2
	}(10, 20)
	fmt.Println("res1", res1)

匿名函数使用方法2

将匿名函数赋给一个函数变量,在通过该变量来调用该匿名函数。

a := func(n1 int, n2 int) int {
		return n1 - n2
	}
	res2 := a(10, 30)
	fmt.Println("res2=", res2)
	res3 := a(90, 30)
	fmt.Println("res3=", res3)

全局匿名函数

如果将匿名函数赋给一个全局变量,那么这个匿名函数,就成为一个全局匿名函数,也可以在程序有效。

//定义一个全局匿名函数
var (
	Fun1 = func(n1 int, n2 int) int {
		return n1 * n2
	}
)

func main(){
res4 := Fun1(4, 9)
	fmt.Println("res4=", res4)
}

闭包

基本介绍

闭包就是一个函数和与其相关的引用环境组合的一个整体(实体)

案例介绍

package main

import "fmt"

// AddUpper 累加器
func AddUpper() func(int) int {
	var n int = 10
	return func(x int) int {
		n = n + x
		return n
	}
}
func main() {
	f := AddUpper()
	fmt.Println(f(1)) // 11
	fmt.Println(f(2)) // 13
	fmt.Println(f(3)) // 16
	fmt.Println(f(1)) // 17

}

对上述代码说明:

  1. AddUpper是一个函数,返回的数据类型是fun(int)int
  2. 闭包的说明
    在这里插入图片描述
    返回的是一个匿名函数,但是该匿名函数引用到函数外的n,因此这个匿名函数就和n形成一个整体,构成了闭包。
  3. 闭包是一个类,函数是操作,n是字段。函数和使用的字段形成一个闭包。
  4. 当我们反复调用函数,n不会重新进行初始化,因此每调用一次就进行累加。
  5. 闭包的关键就是要分析出返回的函数,它使用或引用到哪些变量,以为函数和它引用到的变量共同构成闭包。
  6. 加深对闭包的认识:
package main

import "fmt"

// AddUpper 累加器
func AddUpper() func(int) int {
	var n int = 10
	var str = "hello"
	return func(x int) int {
		n = n + x
		str += string(36)
		fmt.Println("str=", str)
		return n
	}
}
func main() {
	f := AddUpper()
	fmt.Println(f(1)) // 11
	fmt.Println(f(2)) // 13
	fmt.Println(f(3)) // 16
	fmt.Println(f(1)) // 17

}

在这里插入图片描述

函数中-defer

defer介绍

在函数中,程序员经常需要创建资源(比如:数据库连接、文件句柄、锁等),为了在函数执行完毕后,及时释放资源,Go的设计者提供defer(延时机制)。

入门案例

package main

import (
	"fmt"
)

func sum(n1 int, n2 int) int {
	defer fmt.Println("ok1 n1=", n1) //当执行到defer时,暂时不执行,压入到defer的栈中(独立的栈)
	defer fmt.Println("ok2 n2=", n2) //当函数执行完毕后,按照先入后出的方式进行输出
	res := n1 + n2
	fmt.Println("ok3 res=", res)
	return res
}
func main() {
	res := sum(10, 20)
	fmt.Println("res=", res)
}

执行结果:
在这里插入图片描述

defer的细节说明

  1. 当go执行到一个defer时,不会立即执行defer后面的语句,而是将defer后的语句压入到一个栈中,然后继续执行函数下一个语句。
  2. 当函数执行完毕后,再从defer栈中,依次从栈顶去除语句执行(注:遵守栈先入后出的机制)
  3. 当defer将语句放入到栈时,也会将相应的值拷贝同时入栈。
package main

import (
	"fmt"
)

func sum(n1 int, n2 int) int {
	defer fmt.Println("ok1 n1=", n1) //当执行到defer时,暂时不执行,压入到defer的栈中(独立的栈),同时将n1的值拷贝入栈,输出的是n1 ,n2本身的值
	defer fmt.Println("ok2 n2=", n2) //当函数执行完毕后,按照先入后出的方式进行输出

	n1++
	n2++
	res := n1 + n2
	fmt.Println("ok3 res=", res)
	return res
}
func main() {
	res := sum(10, 20)
	fmt.Println("res=", res)
}

defer 的最佳实践

func test(){
	//关闭文件资源
	file = openfile(文件名)
	defer file.close()
	//其它代码
}
func test(){
	//释放数据库资源
	connect = openDatabase()
	defer connect.close()
	//其它代码
}

说明:

  1. 在golang编程中的通常做法是,创建资源后,比如(打开了文件,获取了数据库的连接,或者是锁资源),可以理解defer file.Close() defer connect.Close()
  2. 在defer后,可以继续使用创建资源。
  3. 当函数执行完毕后,系统会依次从defer栈中,取出语句,关闭资源。
  4. 这种机制,非常简洁,不用再为什么时机关闭资源烦心。

函数参数的传递方式

基本介绍

值类型参数默认就是值传递,引用类型参数默认就是引用传递

两种传递方式

  1. 值传递
  2. 引用传递
    说明:
    传递是传递给函数变量的副本,不同的是,值传递是传递值的拷贝,引用传递是地址的拷贝,一般来说,地址拷贝效率高,因为数据量小,而值拷贝决定于拷贝的数据大小,数据越大,效率越低。
  3. 值类型:基本数据类型int系列,float系列,bool,string,数据和结构体struct
  4. 引用类型: 指针、slice切片、map、管道chan、interface等都是引用类型
    1)值类型默认值传递,变量直接存储值,内存通常在栈中分配
    在这里插入图片描述
    2)引用类型默认是引用传递,变量存储的是一个地址,这个地址对应的空间真正存储数据(值),内存通常在堆上分配,当没有任何变量引用这个地址时,改地址对应的数据空间就称为一个垃圾,由GC回收。

在这里插入图片描述
3)如果希望函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量。从效果上看类似引用。

func test03(n1 *int) {
	fmt.Printf("n1的地址 %v\n", &n1)
	*n1 = *n1 + 10
	fmt.Println("test03() n1= ", *n1) // 30 }
}

变量作用域

说明

  1. 函数内部声明/定义的变量叫做局部变量,作用域仅限于函数内部
  2. 函数外部声明/定义的变量叫做全局变量,作用域在整个包中有效,如果首字母大写,则作用域在整个程序中。
  3. 如果变量是在一个代码块中,如for/if中,那么这个变来给的作用域就在该代码块。

函数综合应用

1)函数可以没有返回值案例,编写一个函数,从终端输入一个整数打印出对应的金子塔
分析思路:就是将原来写的打印金字塔的案例,使用函数的方式封装,在需要打印时,直接调用即可。

package main

import "fmt"

// 将打印金字塔的代码封装到函数中
func printPyramid(totalLevel int) {
	//i表示层数
	for i := 1; i <= totalLevel; i++ {
		for k := 1; k <= totalLevel-i; k++ {
			fmt.Print(" ")
		}
		//j表示每层打印多少*
		for j := 1; j <= 2*i-1; j++ {

			fmt.Print("*")

		}
		fmt.Println()
	}
}
func main() {
	//调用printPyramid函数,打印金字塔
	//从终端输出一个整数打印对应的金字塔
	var n int
	fmt.Println("请输入打印金字塔的层数")
	fmt.Scanln(&n)
	printPyramid(n)
}

2)编写一个函数调用九九乘法表

package main

import "fmt"

// 将打印金字塔的代码封装到函数中
func printMulti(num int) {
	for i := 1; i <= num; i++ {
		for j := 1; j <= i; j++ {
			fmt.Printf("%v * %v = %v\t", j, i, j*i)

		}
		fmt.Println()
	}
}
func main() {
	//调用printPyramid函数,打印金字塔
	//从终端输出一个整数打印对应的金字塔
	var n int
	fmt.Println("请输入打印乘法表对应的层数")
	fmt.Scanln(&n)
	printMulti(n)
}

Golang中字符串常用的函数

  1. 统计字符串的长度,按字节len(str)
import "fmt"

func main() {
	//统计字符串的长度,按字节len(str)
	str := "hello中国" //golang的编码统一为utf-8(ASCII的字符(字母和数字)占一个字节,汉字占用三个字节)
	fmt.Println("str len = ", len(str))
}

在这里插入图片描述

  1. 字符串的遍历,同时处理有中文的问题r:=[]rune(str)
str2 := "hello长白山"
	r := []rune(str2)
	for i := 0; i < len(r); i++ {
		fmt.Printf("字符=%c\n", r[i])

	}

在这里插入图片描述

  1. 字符串转整数:m,err:=strconv.Atoi(“12”)
//字符串转整数:m,err:=strconv.Atoi("12")
	n, err := strconv.Atoi("123")
	if err != nil {
		fmt.Println("转换错误", err)
	} else {
		fmt.Println("转成的结果是", n)

	}
  1. 整数转字符串 :str=strconv.Itoa(12345)
str = strconv.Itoa(12345)
	fmt.Printf("str = %v ,str = %T", str, str)

在这里插入图片描述

  1. 字符串转[]byte:var bytes=[]byte(“hello go”)
var bytes = []byte("hello go")
	fmt.Printf("byte=%v\n", bytes)

在这里插入图片描述

  1. []byte转字符串 :str=string([]byte{97,98,99})
 str = string([]byte{97, 98, 99})
	fmt.Printf("str = %v\n", str)

在这里插入图片描述

  1. 10进制转2,8,16进制:str=strconv.FormatInt(123,2)//2->8,16
//10进制转2,8,16进制:str=strconv.FormatInt(123,2)//2->8,16
	str = strconv.FormatInt(123, 16)
	fmt.Printf("str = %v\n", str)

在这里插入图片描述

  1. 查找子串是否在指定的字符串中:strings.Contains(“seafood”,“foo”)//true
//strings.Contains("seafood","foo")//true
	b := strings.Contains("seafood", "foo")
	fmt.Printf("b=%v\n", b)

在这里插入图片描述

  1. 统计一个字符串有几个指定的子串:strings.Count(“ceheese”,“e”)//4
//统计一个字符串有几个指定的子串:strings.Count("ceheese","e")//4
	num := strings.Count("chinese", "e")
	fmt.Printf("num=%v\n", num)

在这里插入图片描述

  1. 不区分大小写的字符转比较(==是区分字母大小写的):fmt.Println(strings.EqualFold(“abc”,“Abc”))//true
d := strings.EqualFold("abc", "Abc")
	fmt.Printf("d=%v\n", d)

在这里插入图片描述

  1. 返回子串在字符串第一次出现的index值,如果没有返回-1:strings.Index(“NLT_abc”,“abc”)//4
index := strings.Index("NLT_abc", "abc")
	index2 := strings.Index("NLT_abc", "hello")
	fmt.Printf("index=%v\n", index)
	fmt.Printf("index=%v\n", index2)

在这里插入图片描述
12. 返回子串在字符串最后一次出现的index,如果没有返回-1:strings.LastIndex(“go golang”,“go”)

index3 := strings.LastIndex("go golang", "go")
	fmt.Printf("index3=%v", index3)

在这里插入图片描述

  1. 将指定的子串替换成另外一个子串:strings.Replace(“go go hello”,“go”,“go语言”,n)n可以指定你希望替换第几个,如果n=-1表示全部替换
 str = "go go hello go"
	str3 := strings.Replace(str, "go", "go语言", -1)
	fmt.Printf("str=%v", str3)

在这里插入图片描述

  1. 按照指定的某个字符,为分割表示,将一个字符串拆分成字符串数组:strings.Split(“hello,world,ok”,“,”)
strArr := strings.Split("hello,world,ok", ",")
	for i := 0; i < len(strArr); i++ {
		fmt.Printf("str[%v]%v\n", i, strArr[i])
	}
	fmt.Printf("strArr%v\n", strArr)

在这里插入图片描述

  1. 将字符串的字母机型呢大小写的转换:strings.ToLower(“Go”)//go strings.ToUpper(“Go”)//Go
str = strings.ToLower("Go") //go strings.ToUpper("Go")
	fmt.Printf("str=%v", str)

在这里插入图片描述

  1. 将字符串左右两边的空格去掉:strings.TrimSpace(" tn a lone gopher ntrn ")
str = strings.TrimSpace("   tn a lone gopher ntrn   ")
	fmt.Printf("str=%v\n", str)

在这里插入图片描述

  1. 将字符串左右两边指定的字符去掉:strings.Trim(“! hello!”,“!”)//[“hello”]//将左右两边的!和" "去掉
str = strings.Trim("! hello!", "!")
	fmt.Printf("str=%v\n", str)

在这里插入图片描述

  1. 将字符串左边指定的字符去掉:strings.TrimLeft(“! hello!”,“!”)//[“hello”]//将左边的!和" "去掉
str = strings.TrimLeft("! hello!", "!")
	fmt.Printf("str=%v\n", str)

在这里插入图片描述

  1. 将字符串右边指定的字符去掉:strings.TrimRight(“! hello!”,“!”)//[“hello”]//将右边的!和" "去掉
str = strings.TrimRight("! hello!", "!")
	fmt.Printf("str=%v\n", str)

在这里插入图片描述

  1. 判断字符串是否以指定的字符串开头:strings.HasPrefix(“ftp://192.168.10.1”,“ftp”)//true
b = strings.HasPrefix("ftp://192.168.10.1", "ftp")
	fmt.Printf("b=%v\n", b)
  1. 判断字符串是否以指定的字符串结束:strings.HasSuffix(“NLT_abc.jpg”,“abc”)//false
b = strings.HasPrefix("NLT_abc.jpg", "abc")
	fmt.Printf("b=%v\n", b)

时间和日期相关的函数

基本介绍

1)时间和日期相关函数,需要导入time包
在这里插入图片描述

2)time.Time类型,用于表示时间

package main

import (
	"fmt"
	"time"
)

func main() {
	now := time.Now()
	fmt.Printf("now=%v now type =%T", now, now)
}

3)获取到当前时间的方法:
now := time.Now() //now的类型就是time.Time
4)如何获取到其它的日期信息

package main

import (
	"fmt"
	"time"
)

func main() {
	now := time.Now()
	fmt.Println("当前年=", now.Year())
	fmt.Println("当前月=", time.Now().Month())
	fmt.Println("当前月=", int(time.Now().Month()))
	fmt.Println("当前日", time.Now().Day())
	fmt.Println("当前时", time.Now().Hour())
	fmt.Println("当前分", time.Now().Minute())
	fmt.Println("当前秒", time.Now().Second())

}

5)格式化日期时间
(1)格式化的第一种方法
now=time.Now()
fmt.Printf(“当前年月日%.2d-%.2d-%.2d %.2d:%.2d:%.2d\n”,
now.Year(),now.Month(),now.Day(),now.Hour(),now.Minute(),now.Second())
(2)格式化的第二种方式
fmt.Printf(now.Format(“2006-01-02 15:04:05”))
fmt.Println()
fmt.Printf(now.Format(“2006-01-01”))
fmt.Println()
fmt.Printf(now.Format(“15:04:05”))
fmt.Println()
6)时间的常量
const (
Nanosecond Duration = 1//纳秒
Microsecond = 1000 * Nanosecond//微秒
Millisecond = 1000 * Microsecond//毫秒
Second = 1000 * Millisecond//秒
Minute = 60 * Second//分钟
Hour = 60 * Minute//小时
)
常量的作用:在程序中可用于获取指定时间单位的时间,比如想得到100毫秒 100*time.Millisecond

案例说明

需求:每个1秒钟打印一个数字,打印到100退出

i := 0
	for {
		i++
		fmt.Println(i)
		time.Sleep(time.Second)
		if i == 100 {
			break
		}
	}

8)获取当前unix时间戳和unixnano时间戳,(作用是可以获取随机时间)

	fmt.Printf("unix时间戳=%v unixnano时间戳=%v", now.Unix(), now.UnixNano())

编写一段代码来统计 函数test03执行的时间

package main

import (
	"fmt"
	"strconv"
	"time"
)

func test03() {
	str := ""
	for i := 0; i < 100000; i++ {
		str += "hello" + strconv.Itoa(i)
	}
}
func main() {
	//在执行test03前先获取到当前的unix时间戳
	start := time.Now().Unix()
	test03()
	end := time.Now().Unix()
	fmt.Printf("执行test03()耗费的时间是%v秒\n", end-start)
}

内置函数(buildin)

说明:Golang设计者为了编程方便,提供了一些函数,这些函数可以直接使用,我们称为Go的内置函数。

  1. len:用来求长度,比如:string、array、slice、map、channel
  2. new:用来分配内存,主要用来分配值类型,比如int、float32,struct…返回的是指针
package main

import "fmt"

func main() {
	num1 := 100
	fmt.Printf("num1的类型是%T,num1的值%v,num1的地址时%v\n", num1, num1, &num1)
	num2 := new(int)
	*num2 = 100
	fmt.Printf("num2的类型是%T,num2的值%v,num2的地址时%v,num2指向的值是=%v", num2, num2, &num2, *num2)
}

  1. make:用来分配内存,主要是用来分配引用类型,比如channel、map、slice。

Golang中的错误处理

  1. 在默认情况下,当发生错误后(panic),程序就会退出(崩溃)
  2. 如果我们希望, 当发生错误后,可以捕获到错误,并进行处理,保证程序可以继续进行。还可以在捕获到错误后,给管理员一个提示(邮件或者短信)

基本说明

  1. Go语言追求简洁优雅,所以,go语言不支持传统的try…catch…finally这种处理。
  2. Go中引入的处理方式为:defer,panic,recover
  3. Go中可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理。

使用defer+recover方式捕获和处理异常

package main

import "fmt"

func test() {
	defer func() {
		err := recover()
		if err != nil {
			fmt.Println("err=", err)
		}
	}()
	num1 := 10
	num2 := 0
	res := num1 / num2
	fmt.Println("res=", res)
}
func main() {
	test()
	fmt.Println("main()下面的代码...")
}

在这里插入图片描述

错误处理的好处

进行错误处理后,程序不会轻易挂掉,如果加入预警代码,就可以让程序更加的健壮。
案例演示:

package main

import "fmt"

func test() {
	defer func() {
		err := recover()
		if err != nil {
			fmt.Println("err=", err)
			fmt.Println("发送邮件给admin@qq.com")
		}
	}()
	num1 := 10
	num2 := 0
	res := num1 / num2
	fmt.Println("res=", res)
}
func main() {
	test()
	fmt.Println("main()下面的代码...")
}

自定义错误

自定义错误介绍

Go程序中,也支持自定义错误,使用errors.New和panic内置函数

  1. errors.New(“错误说明”),会返回一个error类型的值,表示一个错误
  2. panic内置函数,接收一个interface{}类型的值(也就是任何值)作为参数。可以接收error类型的变量,输出错误信息,并退出程序。

案例

package main

import (
	"errors"
	"fmt"
)

func test() {
	defer func() {
		err := recover()
		if err != nil {
			fmt.Println("err=", err)
			fmt.Println("发送邮件给admin@qq.com")
		}
	}()
	num1 := 10
	num2 := 0
	res := num1 / num2
	fmt.Println("res=", res)
}
func readConf(name string) (err error) {
	if name == "config.ini" {
		return nil
	} else {
		return errors.New("读取文件错误")
	}
}
func test02() {
	err := readConf("config2.ini")
	if err != nil {
		//如果读取文件发生错误,就输出错误并终止程序
		panic(err)

	}
	fmt.Println("test02()继续执行")
}
func main() {
	//测试自定义错误使用
	test02()
	fmt.Println("main()下面的代码")
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值