第六章 函数
函数介绍
为完成某一个功能的程序指令(语句)的集合,称为函数
在Go中,函数分为:自定义函数、系统函数。
函数的定义
- 基本语法
func 函数名(形参列表)(返回值列表){
执行语句…
return返回值列表
}
- 形参列表:表示函数的输入
- 函数中的语句:表示为了实现某一功能的代码块
- 函数可以与返回值,也可以没有
- 案例分析
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是以包的形式来管理文件和项目目录结构的
- 包的三大作用
- 区分相同名字的函数、变量等标识符
- 当程序文件很多时,可以很好的管理项目
- 控制函数、变量等访问范围,即作用域
- 包的使用
- 打包基本语法
package 包名 - 引入包的基本语法
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)
}
- 包的注意事项和使用细节
- 在给一个文件打包时,该包对应一个文件夹,比如utils文件夹对应的就是utils,文件的包名通常和文件所在的文件夹名一致,一般用小写字母。
- 当一个文件要使用其它包的函数或者变量时,应该先引入对应的包
1)package指令在文件第一行,然后是import指令
2)在import宝石,路径从$GOPATH的src下开始,不用带src - 为了让其它包的文件,可以访问到本宝的函数,则该函数名的首字母需要大写,类似其它语言中的public,这样才能挎包访问。
func Cal(n1 float64, n2 float64, operator byte) float64 {
}
- 在访问其它包函数是变量时,其语法是包名.函数名。比如:
utils.Cal(n1,n2,"+")
- 如果包名较长,go语言支持给包取别名,注意:取别名后,原来的包名不能使用
import (
"fmt"
util "go_code/chapter06/fundemo01/utils"//引入utils中的函数
)
如果给包取了别名,则需要使用别名来访问该包的函数和变量。
- 在同一个包下,不能有相同的函数名,否则报重复定义。
- 如果要编译成一个可执行程序文件,就需要将这个包声明为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返回值列表
}
- 如果返回多个值,在接收时,希望忽略某个函数值,使用_符号表示占位忽略
- 如果返回值只有一个,(返回值类型列表)可以不写()
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)
}
- 函数递归需要遵守的重要原则:
- 执行一个函数时,就创建一个新的受保护的独立空间(新函数栈)
- 函数的局部变量是独立的,不会相互影响
- 递归必须向退出递归的条件逼近,否则就是无限递归
- 当一个函数执行完毕,或者遇到return,就会返回,
遵守谁调用,就将结果返回给谁
,当函数执行完毕或者返回时,该函数本身就会被编译器销毁。
- 案例演示
- 使用递归的方法,输入一个整数,求出对应的斐波那契数
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)
}
- 求函数值,已知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)
}
- 题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))
}
函数注意事项和细节
- 函数的形参列表可以是多个,返回值列表也可以是多个
- 形参列表和返回值列表的数据类型可以是值类型和引用类型
- 函数的命名遵循标识符命名规范,首字母不能是数字,首字母大写该函数可以被本包文件和其它包文件使用,首字母小写,只能被本报文件使用,其它包文件不能使用
- 函数中的变量是局部的,函数外不生效
- 基本数据类型和数组默认都是值传递的,即进行值拷贝。在函数内修改,不会影响到原来的值
- 如果希望函数内的变来给你能修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量。从效果上类似引用。
- Go函数不支持重载
- 在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)
}
- 函数既然是一种数据类型,因此在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)
}
- 为了减缓数据类型定义,Go支持自定义数据类型
基本语法:type 自定义数据类型名 数据类型 //理解:相当于一个别名
案例:type myInt int //myInt等价于int使用(注意:go认为myInt和int是两个类型)
案例:type mySum func(int,int)int // mySum等价于func(int,int)int使用 - 支持对函数返回值命名
func cal(n1 int ,n2 int)(sum int ,sub int){
sum = n1 + n2
sub = n1 - n2
return
}
- 使用_标识符,忽略返回值
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)
}
- 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函数的注意事项和细节
- 如果一个文件中同时包含
全局变量定义
,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)
}
- 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
}
对上述代码说明:
- AddUpper是一个函数,返回的数据类型是fun(int)int
- 闭包的说明
返回的是一个匿名函数,但是该匿名函数引用到函数外的n,因此这个匿名函数就和n形成一个整体,构成了闭包。 - 闭包是一个类,函数是操作,n是字段。函数和使用的字段形成一个闭包。
- 当我们反复调用函数,n不会重新进行初始化,因此每调用一次就进行累加。
- 闭包的关键就是要分析出返回的函数,它使用或引用到哪些变量,以为函数和它引用到的变量共同构成闭包。
- 加深对闭包的认识:
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的细节说明
- 当go执行到一个defer时,不会立即执行defer后面的语句,而是将defer后的语句压入到一个栈中,然后继续执行函数下一个语句。
- 当函数执行完毕后,再从defer栈中,依次从栈顶去除语句执行(注:遵守栈先入后出的机制)
- 当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()
//其它代码
}
说明:
- 在golang编程中的通常做法是,创建资源后,比如(打开了文件,获取了数据库的连接,或者是锁资源),可以理解defer file.Close() defer connect.Close()
- 在defer后,可以继续使用创建资源。
- 当函数执行完毕后,系统会依次从defer栈中,取出语句,关闭资源。
- 这种机制,非常简洁,不用再为什么时机关闭资源烦心。
函数参数的传递方式
基本介绍
值类型参数默认就是值传递,引用类型参数默认就是引用传递
两种传递方式
- 值传递
- 引用传递
说明:
传递是传递给函数变量的副本,不同的是,值传递是传递值的拷贝,引用传递是地址的拷贝,一般来说,地址拷贝效率高,因为数据量小,而值拷贝决定于拷贝的数据大小,数据越大,效率越低。 - 值类型:基本数据类型int系列,float系列,bool,string,数据和结构体struct
- 引用类型: 指针、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 }
}
变量作用域
说明
- 函数内部声明/定义的变量叫做局部变量,作用域仅限于函数内部
- 函数外部声明/定义的变量叫做全局变量,作用域在整个包中有效,如果首字母大写,则作用域在整个程序中。
- 如果变量是在一个代码块中,如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中字符串常用的函数
- 统计字符串的长度,按字节len(str)
import "fmt"
func main() {
//统计字符串的长度,按字节len(str)
str := "hello中国" //golang的编码统一为utf-8(ASCII的字符(字母和数字)占一个字节,汉字占用三个字节)
fmt.Println("str len = ", len(str))
}
- 字符串的遍历,同时处理有中文的问题r:=[]rune(str)
str2 := "hello长白山"
r := []rune(str2)
for i := 0; i < len(r); i++ {
fmt.Printf("字符=%c\n", r[i])
}
- 字符串转整数: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)
}
- 整数转字符串 :str=strconv.Itoa(12345)
str = strconv.Itoa(12345)
fmt.Printf("str = %v ,str = %T", str, str)
- 字符串转[]byte:var bytes=[]byte(“hello go”)
var bytes = []byte("hello go")
fmt.Printf("byte=%v\n", bytes)
- []byte转字符串 :str=string([]byte{97,98,99})
str = string([]byte{97, 98, 99})
fmt.Printf("str = %v\n", str)
- 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)
- 查找子串是否在指定的字符串中:strings.Contains(“seafood”,“foo”)//true
//strings.Contains("seafood","foo")//true
b := strings.Contains("seafood", "foo")
fmt.Printf("b=%v\n", b)
- 统计一个字符串有几个指定的子串:strings.Count(“ceheese”,“e”)//4
//统计一个字符串有几个指定的子串:strings.Count("ceheese","e")//4
num := strings.Count("chinese", "e")
fmt.Printf("num=%v\n", num)
- 不区分大小写的字符转比较(==是区分字母大小写的):fmt.Println(strings.EqualFold(“abc”,“Abc”))//true
d := strings.EqualFold("abc", "Abc")
fmt.Printf("d=%v\n", d)
- 返回子串在字符串第一次出现的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)
- 将指定的子串替换成另外一个子串: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)
- 按照指定的某个字符,为分割表示,将一个字符串拆分成字符串数组: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)
- 将字符串的字母机型呢大小写的转换:strings.ToLower(“Go”)//go strings.ToUpper(“Go”)//Go
str = strings.ToLower("Go") //go strings.ToUpper("Go")
fmt.Printf("str=%v", str)
- 将字符串左右两边的空格去掉:strings.TrimSpace(" tn a lone gopher ntrn ")
str = strings.TrimSpace(" tn a lone gopher ntrn ")
fmt.Printf("str=%v\n", str)
- 将字符串左右两边指定的字符去掉:strings.Trim(“! hello!”,“!”)//[“hello”]//将左右两边的!和" "去掉
str = strings.Trim("! hello!", "!")
fmt.Printf("str=%v\n", str)
- 将字符串左边指定的字符去掉:strings.TrimLeft(“! hello!”,“!”)//[“hello”]//将左边的!和" "去掉
str = strings.TrimLeft("! hello!", "!")
fmt.Printf("str=%v\n", str)
- 将字符串右边指定的字符去掉:strings.TrimRight(“! hello!”,“!”)//[“hello”]//将右边的!和" "去掉
str = strings.TrimRight("! hello!", "!")
fmt.Printf("str=%v\n", str)
- 判断字符串是否以指定的字符串开头:strings.HasPrefix(“ftp://192.168.10.1”,“ftp”)//true
b = strings.HasPrefix("ftp://192.168.10.1", "ftp")
fmt.Printf("b=%v\n", b)
- 判断字符串是否以指定的字符串结束: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的内置函数。
- len:用来求长度,比如:string、array、slice、map、channel
- 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)
}
- make:用来分配内存,主要是用来分配引用类型,比如channel、map、slice。
Golang中的错误处理
- 在默认情况下,当发生错误后(panic),程序就会退出(崩溃)
- 如果我们希望, 当发生错误后,可以捕获到错误,并进行处理,保证程序可以继续进行。还可以在捕获到错误后,给管理员一个提示(邮件或者短信)
基本说明
- Go语言追求简洁优雅,所以,go语言不支持传统的try…catch…finally这种处理。
- Go中引入的处理方式为:defer,panic,recover
- 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内置函数
- errors.New(“错误说明”),会返回一个error类型的值,表示一个错误
- 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()下面的代码")
}