书接上篇:极速Go语言入门(超全超详细)-基础篇
整个基础篇合计32000字左右,如有遗漏可以私信我进行补充
以下展示函数return的使用、普通函数、匿名函数、闭包、defer延迟执行、函数作为参数传递、函数作为函数返回值、内置函数
go内置函数可参考: 中文官方文档中**builtin**(点击跳转)一栏
示例代码
package mainimport ("fmt""strconv"
)func main() {println("-------return测试---------")result_1, result_2 := returnTest(88, 99)result_3, result_4 := returnTestV2(88, 99)fmt.Printf("returnTest执行结果, result_1 = %v, result_2 = %v \n", result_1, result_2)fmt.Printf("returnTestV2执行结果, result_3 = %v, result_4 = %v \n", result_3, result_4)println("-------函数测试---------")testFuntion(1)funcResultV2_1, funcResultV2_2 := testFuntionV2(1, "123")fmt.Printf("funcResultV2_1 = %v, funcResultV2_2 = %v \n", funcResultV2_1, funcResultV2_2)testFuntionV3()testFuntionV3(1, 2, 3, 4, 5)println("-------匿名函数测试---------")//匿名函数,没有名字的函数,放在代码块中直接执行anonymousFuncResult_1, _ := func(n1 int, n2 int) (int, float64) {return n1 * n2, float64(n1 * n2)}(2, 8) //尾部的括号里传递参数fmt.Println("anonymousFuncResult = ", anonymousFuncResult_1)//也可以直接把匿名函数赋值给变量,但是赋值给变量之前不能给匿名函数传递参数a := func(n1 int, n2 int) (int, int) {return n2, n1} //尾部没有参数n1 := 10n2 := 29n1, n2 = a(n1, n2)fmt.Printf("匿名函数赋值给变量之后n1 = %v, n2 = %v \n", n1, n2)println("-------全局匿名函数测试---------")//调用定义好的匿名函数全局变量globalVariableFuncResult_1, globalVariableFuncResult_2 := globalVariableFunc(1, 3)fmt.Printf("调用全局匿名函数结果globalVariableFuncResult_1 = %v type: %T, globalVariableFuncResult_1 = %v type: %T\n",globalVariableFuncResult_1, globalVariableFuncResult_1, globalVariableFuncResult_2, globalVariableFuncResult_2)//闭包调用f2 := closureTest()//依下述输出来看,虽然我们没有显性的声明一个全局变量,但是我们每次调用都会进行累加fmt.Printf("闭包调用第1次返回结果:%v \n", f2(10))fmt.Printf("闭包调用第2次返回结果:%v \n", f2(10))fmt.Printf("闭包调用第3次返回结果:%v \n", f2(10))f3 := closureTest()fmt.Printf("新的闭包调用第1次返回结果:%v \n", f3(10)) //新的实例运行后并不会原先闭包函数中的变量println("-------defer测试---------")//defer:defer是go中一种延迟调用机制,defer后面的函数只有在当前函数执行完毕后才能执行,通常用于释放资源。//参考资料:https://blog.csdn.net/m0_46251547/article/details/123762669deferTest("字符参数")println("-------函数作为参数测试---------")testFuncArg(printString)println("-------函数作为返回值测试---------")returnFunc := testReturnFunc()fmt.Printf("函数作为返回值测试, 返回结果类型 = %T \n", returnFunc)finalResult := returnFunc(100, 200)fmt.Printf("执行函数, 结果 = %d, 类型 = %T \n", finalResult, finalResult)println("-------内置函数测试---------")//1. len : 用来求长度,比如string、array、slice、map、channel//2. new : 用来分配内存,主要用来分配值类型,比如int、float32、struct…返回的是指针//3. make:用来分配内存,主要用来分配引用类型,比如chan、map、slice。//值类型的用new,返回的是一个指针p := new(int)fmt.Println("*p = ", *p, ", p = ", p)*p = 29fmt.Println("*p = ", *p)//引用类型的用makestringStringMap := make(map[string]string, 10)//向map增加元素stringStringMap["name"] = "Mir Li"stringStringMap["age"] = "18"}/** 如下所示,函数定义
func 函数名 (参数列表) (返回值列表) { //返回值只有一个时可以不写()//函数体,功能执行return 返回值列表}
*/
// ///return///
func returnTest(n1 int64, n2 int64) (string, int64) {//return:用于函数执行结果的值返回,可以返回一个或者多个参数result_1 := strconv.FormatInt(n1*n2, 10)result_2 := n1 + n2return result_1, result_2
}// 上面的写法还可以这些写
func returnTestV2(n1 int64, n2 int64) (result_1 string, result_2 int64) {result_1 = strconv.FormatInt(n1*n2, 10)result_2 = n1 + n2return
}// //普通函数//
// 单个入参、单个出参
func testFuntion(number int) int { //()里的是入参, {左边的是返回值类型和参数, 可以写(result, int)return number * 10
}func testFuntionV2(number int, value string) (a int, b string) {//转化成10进制,进行字符串拼接resultStr := strconv.FormatInt(int64(number), 10) + valuereturn number, resultStr
}// 多个入参、单个出参
func testFuntionV3(args ...int) (int, string, float64) { //(args ...int)表示可以传递多个参数,可以理解为0到多个参数resultNum := 0for arg := range args {fmt.Println("打印参数arg = ", arg)resultNum += arg}resultStr := "返回值2"resultFloatNum := 888.88fmt.Printf("最终返回值1=%v, 最终返回值2=%v, 最终返回值3=%v \n", resultNum, resultStr, resultFloatNum)return resultNum, resultStr, resultFloatNum
}// init函数,最大的作用是用来初始化源文件,该函数会在main函数执行前被调用
func init() {//do somethingfmt.Println("初始化函数,先于main函数执行")
}// ///匿名函数(全局变量)///
var (globalVariableFunc = func(number_1 int, number_2 int) (int, string) {return number_2 * number_1, strconv.FormatInt(int64(number_1), 10)}
)// ///闭包///
// 含义:闭包是由函数和与其相关的引用环境组合而成的实体[抽象、难以理解!],其实就是:匿名函数+外部引用
// 为什么使用闭包(为了避免全局变量被滥用):可以让变量常驻内存、可以让变量不污染全局
// 可参考文章:https://blog.csdn.net/qq_27654007/article/details/116667624
func closureTest() func(int) int {var n int = 10return func(x int) int {//每一次调用都会给n进行累加赋值,n的值在内存中伴随整个闭包实例的整个生命周期n = n + xreturn n}
}// ///defer///
func deferTest(strValue string) string {//依据打印输出可以看出来,defer这行的逻辑是在return那一刻执行的defer printString("defer 延迟执行测试")printString("deferTest 执行测试-1")printString("deferTest 执行测试-2")return strValue
}func printString(str string) {fmt.Println(str)
}// ///函数作为参数/
func testFuncArg(param func(str string)) {param("函数作为参数测试")
}// ///函数作为返回值/
func testReturnFunc() func(result_1 int64, result_2 int64) int64 {return func(n1 int64, n2 int64) int64 {return n1 * n2}
}
运行结果
初始化函数,先于main函数执行
-------return测试---------
returnTest执行结果, result_1 = 8712, result_2 = 187
returnTestV2执行结果, result_3 = 8712, result_4 = 187
-------函数测试---------
funcResultV2_1 = 1, funcResultV2_2 = 1123
最终返回值1=0, 最终返回值2=返回值2, 最终返回值3=888.88
打印参数arg = 0
打印参数arg = 1
打印参数arg = 2
打印参数arg = 3
打印参数arg = 4
最终返回值1=10, 最终返回值2=返回值2, 最终返回值3=888.88
-------匿名函数测试---------
anonymousFuncResult = 16
匿名函数赋值给变量之后n1 = 29, n2 = 10
-------全局匿名函数测试---------
调用全局匿名函数结果globalVariableFuncResult_1 = 3 type: int, globalVariableFuncResult_1 = 1 type: string
闭包调用第1次返回结果:20
闭包调用第2次返回结果:30
闭包调用第3次返回结果:40
新的闭包调用第1次返回结果:20
-------defer测试---------
deferTest 执行测试-1
deferTest 执行测试-2
defer 延迟执行测试
-------函数作为参数测试---------
函数作为参数测试
-------函数作为返回值测试---------
函数作为返回值测试, 返回结果类型 = func(int64, int64) int64
执行函数, 结果 = 20000, 类型 = int64
-------内置函数测试---------
*p = 0 , p = 0x140000a6030
*p = 29
之前的变量只能定义一个使用一个,有时候可能需要很多个变量,当出现这种情况的时候我们可以使用struct结构体,
struct定义结构,结构由字段(field)组成,每个field都有所属数据类型,在一个struct中,每个字段名都必须唯一。
Go中不支持面向对象,面向对象中描述事物的类的重担由struct来挑。比如面向对象中的继承,可以使用组合(composite)来实现:struct中嵌套一个(或多个)类型。
结构如下
type UserInfo struct {field1 type1 field2 type2…
}// 或者
type T struct { a, b int }
理论上,每个字段都是有具有唯一性的名字的,但如果确定某个字段不会被使用,可以将其名称定义为空标识符_
来丢弃掉:
type T struct {_ stringa int
}
示例代码
package mainimport ("fmt"
)type UserInfo struct {//用户姓名name string//年龄、身高age, height int16 //相同类型可以定义在一行//用户地址address string
}// 如果里面的字段类型都一样可以使用这种方法
type UserInfoV2 struct{ name, address string }// 组合型
type UserInfoV3 struct {user_1 UserInfouser_2 UserInfoV2
}func main() {//初始化结构体info := UserInfo{age: 18, name: "小明", height: 180, address: "浙江省杭州市"}fmt.Printf("结构体信息:%v \n", info)//访问结构体属性,info.xxxfmt.Printf("访问name信息:%v, age:%v, 访问height信息:%v, 访问address信息:%v \n", info.name, info.age, info.height, info.address)//分配内存地址连续fmt.Printf("name分配内存地址:%v, age分配内存地址:%v, height分配内存地址:%v, address分配内存地址:%v \n", &info.name, &info.age, &info.height, &info.address)//组合型结构体user_1 := UserInfo{age: 33, name: "Mir Zhang", height: 180, address: "北京"}user_2 := UserInfoV2{name: "Mir Li", address: "北京"}user_3 := UserInfoV3{user_1, user_2}fmt.Printf("结构体信息:%s \n", user_3)}
运行结果
初始化函数,先于main函数执行
结构体信息:{小明 18 180 浙江省杭州市}
name:小明, age:18, height:180, address:浙江省杭州市
name分配内存地址:0x14000112180, age分配内存地址:0x14000112190, height分配内存地址:0x14000112192, address分配内存地址:0x14000112198
结构体信息:{{Mir Zhang %!s(int16=33) %!s(int16=180) 北京} {Mir Li 北京}}
interface类型可以定义一组方法,不需要实现。并且interface不能包含任何变量。
Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。
就是定义一个规范,让实现接口的实例按照这个规范来实现自己的逻辑
以以下示例来讲,定义一个动物接口,里面有发出声音的接口,实现接口的动物实例按照各自的业务逻辑来实现这个发出声音的方法
代码示例
package mainimport "fmt"func main() {//根据不同的实例调用对应的实例方法doVoice(Dark{name: "小鸭子"})doVoice(new(Cat))}// 定义一个接口
type Animal interface { // type 接口名 interfacevoice()
}// 定义结构体
type Dark struct {name string
}type Cat struct {name string
}// 实现接口方法
func (dark Dark) voice() { // 接口是引用类型,所以这里传递的是变量的引用fmt.Printf("%v 嘎嘎叫 \n", dark.name)
}func (cat Cat) voice() { // 接口是引用类型,所以这里传递的是变量的引用fmt.Printf("喵喵叫 \n")
}func doVoice(animal Animal) {animal.voice()
}
运行结果
小鸭子 嘎嘎叫
喵喵叫
在java中继承相当于拥有父类的一些属性和方法
go里面继承可以通过嵌套匿名结构体来实现
代码示例
package mainimport ("fmt"
)func main() {father := Father{name: "父亲", phone: "13100001111"}fmt.Printf("父亲属性: %d \n", father)son := new(Son)son.hobby = "打篮球"son.name = "儿子"son.phone = "13033331111"fmt.Printf("儿子属性: %d \n", son)}type Father struct {name, phone string
}type Son struct {Father //匿名结构体hobby string
}
运行结果
父亲属性: {%!d(string=父亲) %!d(string=13100001111)}
儿子属性: &{{%!d(string=儿子) %!d(string=13033331111)} %!d(string=打篮球)}
type用于类型定义(type definition)与类型别名(type alias),可以理解就是定义一个类型或者给一个类型起别名
示例代码
package mainimport ("fmt""unsafe"
)// 定义类型
type UserMsg struct {name, phone string
}// 类型起别名
type myIntType int64//结构体起别名
type MyUserMsg UserMsgfunc main() {//使用起了别名的类型var number_test myIntType = 999fmt.Printf("number_test = %v, 类型:%T, 大小:%v字节 \n", number_test, number_test, unsafe.Sizeof(number_test))myUserMsg := MyUserMsg{name: "123", phone: "13111110000"}fmt.Printf("myUserMsg = %v, 类型:%T, 大小:%v字节 \n", myUserMsg, myUserMsg, unsafe.Sizeof(myUserMsg))}
运行结果
number_test = 999, 类型:main.myIntType, 大小:8字节
myUserMsg = {123 13111110000}, 类型:main.MyUserMsg, 大小:32字节
值类型:基本数据类型、数组、结构体。变量直接存储值,通常存储于栈中,函数传参时使用值传递
引用类型:指针、切片、映射、管道、接口等。变量存储的是值的地址,通常存储于堆中,会发生GC,函数传参时使用引用传递。
值传递:使用按值传递来传递参数,也就是传递参数的副本。在函数中对副本的值进行更改操作时,不会影响到原来的变量。
引用传递:传递的是一个地址的拷贝,通过它可以修改这个值所指向的地址上的值。
在函数调用时,引用类型(slice、map、interface、channel)都默认使用引用传递,另外使用指针也可以进行引用传递。
package mainimport ("fmt""unsafe"
)func main() {//值传递测试var number int64 = 999testV1(number)//从这里看出调用函数赋值后并不会改变原值fmt.Println("main()当前number=", number)//应用传递测试var i int = 10fmt.Printf("i当前的值 = %v \n", i)//1、ptr是一个指针变量 2、ptr存的是i变量的地址 3、类型是*intvar ptr *int = &i//赋予新值*ptr = 20fmt.Printf("指针存储的值:%v, 类型:%T, 占内存字节数:%d, 指针存储地址指向的值:%d, 指针的地址:%v \n", ptr, ptr, unsafe.Sizeof(ptr), *ptr, &ptr)fmt.Printf("i修改后的值 = %v \n", i)
}func testV1(number int64) {number = 1000fmt.Println("testV1()当前number=", number)
}
运行结果
testV1()当前number= 1000
main()当前number= 999
i当前的值 = 10
指针存储的值:0x14000114018, 类型:*int, 占内存字节数:8, 指针存储地址指向的值:20, 指针的地址:0x14000100020
i修改后的值 = 20
有时候我们除了引用go提供的官方包,也可能需要我们自己打包后在其他模块引用封装好的包
如我当前项目文件名为:MyTest
在项目文件路径下创建了packageUtils文件夹,在文件夹下创建了UtilsPKG.go文件
图示如下:
package packageUtilsfunc NumberUtils(number_1 int64, number_2 int64, operation string) (resultNumber float64) {switch operation {case "*":resultNumber = float64(number_2 * number_1)breakcase "/":resultNumber = float64(number_2 / number_1)breakcase "*+":resultNumber = float64(number_2 + number_1)breakcase "-":resultNumber = float64(number_2 - number_1)break}return
}
在其他路径下随意创建一个文件
package mainimport ("MyTest/packageUtils" //这块就是导入了我们上面打好的包"fmt"
)func main() {resultNum := packageUtils.NumberUtils(100, 200, "*")fmt.Printf("执行包调用函数结果:%v", resultNum)
}
运行结果
包调用函数返回值: 200
方法虽不同于函数,是两个概念性的东西,但是方法和函数有些相似,如果之前没有接触过go方法和函数相关的知识,一定会犯迷糊,下面我们看看方法的用法,go中方法是作用在指定的数据类型上的(即:和指定的数据类型绑定),因此自定义类型,都可以有方法,而不仅仅是struct。
go方法的声明(定义)
方法必须要有一个接收者(t type),这个接收者是一个类型,这样方法就和这个类型绑定在一起,称为这个类型的方法。
func (t type) methodName (参数列表) (返回值列表){方法体return 返回值
}
代码示例
package mainimport ("fmt""strconv""unsafe"
)func main() {var number_test MyInt = 888result_num := number_test.testMethod(999)println("testMethod invoke result:", result_num)personInfo := Person{Age: 20, Name: "Mr F"}result := personInfo.testMethodV2()fmt.Printf("testMethodV2 invoke result:%v, type:%T", result, result)}type MyInt int64func (receiver MyInt) testMethod(number_1 int64) string {fmt.Println("this is a data type receiver,", receiver)return "-------" + strconv.FormatInt(number_1, 10) + "-------"
}type Person struct {Name stringAge int
}func (receiver Person) testMethodV2() Person {fmt.Println("this is a struct receiver,", receiver)return receiver
}
运行结果
this is a data type receiver, 888
testMethod invoke result: -------999-------
this is a struct receiver, {Mr F 20}
testMethodV2 invoke result:{Mr F 20}, type:main.Person
go语言中没有像java中一样的try catch finally处理模块,
代码示例
package mainimport ("fmt""strconv""strings""unsafe"
)func main() {//测试异常捕捉处理testExceptionCapture();fmt.Println("异常捕捉后继续处理流程")//测试异常panictestPanic();//上面函数执行报错后如果没有异常捕捉处理程序直接结束运行fmt.Println("异常捕捉处理测试")
}func testPanic() {n1 := 1n2 := 0n3 := n1 / n2//发送异常之后,下面的输出语句不会输出,程序直接结束fmt.Println("res:", n3)
}func testExceptionCapture() {defer func() {if err := recover(); err != nil {fmt.Println("捕获到的异常信息: ", err)//异常之后做一些事情fmt.Println("发送钉钉告警或者邮件告警等")}}()n1 := 1n2 := 0n3 := n1 / n2//发送异常之后,下面的输出语句不会输出fmt.Println("res:", n3)
}
运行结果
捕获到的异常信息: runtime error: integer divide by zero
发送钉钉告警或者邮件告警等
异常捕捉后继续处理流程
panic: runtime error: integer divide by zerogoroutine 1 [running]:
main.testPanic()/Users/dasouche/go/src/MyTest/TwoTest.go:27 +0x1c
main.main()/Users/dasouche/go/src/MyTest/TwoTest.go:18 +0x64
参考中文文档strings包相关:https://studygolang.com/static/pkgdoc/pkg/strings.htm
代码示例
package mainimport ("fmt""strconv""strings""time""unsafe"
)func main() {//统计字符串的长度,按字节 len(str)var str string = "hello word"fmt.Printf("str长度:%v \n", len(str))//字符串遍历,同时处理有中文的问题 r := []rune(str)r := []rune(str)for i := range r {fmt.Printf("遍历字符串:%v \n", i)}//字符串转整数: n , err := strconv.Atoi(“12”)number_1, _ := strconv.Atoi("12")number_2, err_2 := strconv.Atoi("test")fmt.Printf("字符串转整数,number_1:%v \n", number_1)fmt.Printf("字符串转整数,number_2:%v, 错误信息:%v \n", number_2, err_2)//整数转字符串: str = strconv.Itoa(12345)、strconv.FormatInt(999, 10)str = strconv.Itoa(12345)fmt.Printf("整数转字符串,str:%v \n", str)str = strconv.FormatInt(999, 10)fmt.Printf("整数转字符串,str:%v \n", str)//字符串转[]byte: var bytes= []byte(“hello go”)var bytes = []byte("hello word")fmt.Printf("字符串转byte数组,str:%v \n", bytes)//[]byte转字符串: str = string([]byte{97,98,99})str = string([]byte{97, 98, 99})fmt.Printf("byte转字符串,str:%v \n", str)//10进制转2,8,16进制: str = strconv.FormatInt(123,2) // 2->8,16Sstr = strconv.FormatInt(123, 2)fmt.Printf("数字转2进制字符串,str:%v \n", str)str = strconv.FormatInt(123, 8)fmt.Printf("数字转8进制字符串,str:%v \n", str)str = strconv.FormatInt(123, 10)fmt.Printf("数字转10进制字符串,str:%v \n", str)str = strconv.FormatInt(123, 16)fmt.Printf("数字转16进制字符串,str:%v \n", str)//查找子串是否在指定的字符串中: strings.Contains(“seafood”, “foo”) //trueisContains := strings.Contains("food foo eat", "foo")fmt.Printf("是否包含字符串foo,isContains:%v \n", isContains)//统计一个字符串有几个指定的子串:strings.Count(“ceheese”, “e”) //4countNum := strings.Count("hello word", "l")fmt.Printf("字符串含有%d个l:%v \n", countNum)//不区分大小写的字符串比较(== 是区分字母大小写的): fmt.PrintIn(strings.EqualFold(“abc”, “Abc”) // trueisEqual := strings.EqualFold("abc", "Abc")fmt.Printf("字符串不区分大小写比较是否相等:%v \n", isEqual)fmt.Printf("字符串区分大小写比较是否相等:%v \n", "abc" == "Abc")//返回子串在字符串第一次出现的index值,如果没有返回-1 : strings.Index(“NLT_abc”,”abc”) //4firstIndex := strings.Index("hello word hello word", "ll")fmt.Printf("字符子串在字符串中第一次出现的地方:%v \n", firstIndex)//返回子串在字符串最后一次出现的index,如没有返回-1 : strings.LastIndex(“go golang” , “go”)lastIndex := strings.LastIndex("hello word hello word", "ll")fmt.Printf("字符子串在字符串中最后一次出现的地方:%v \n", lastIndex)//将指定的子串替换成 另外一个子串: strings.Replace(“go go hello” , “go”, “go语言”, n) n 可以指定你希望替换几个,如果n = -1表示全部替换str = strings.Replace("hello word hello word", "ll", "ll-", 2)fmt.Printf("替换后的字符串:%v \n", str)//按照指定的某个字符,为分割标识,将一个字符串拆分成字符串数组:splitData := strings.Split("Mr Li,20,北京朝阳区", ",")fmt.Printf("分割后的数据:%v \n", splitData)//将字符串的字母进行大小写的转换: strings.ToLower(“Go”) // go strings.ToUpper(“Go”) //GOstr = strings.ToLower("HELLO Word")fmt.Printf("转换成小写后的数据:%v \n", str)str = strings.ToUpper("hello Word")fmt.Printf("转换成大写后的数据:%v \n", str)//将字符串左右两边的空格去掉 : strings.TrimSpace(“ tn a lone gopher ntrn “)str = strings.TrimSpace(" hello, word ")fmt.Printf("去掉空格后的数据:%v \n", str)//将字符串左右两边指定的字符去掉: strings.Trim(“! hello! “, “ !”) // [“hello”]//将左右两边!和””去掉str = strings.Trim("! hello, word !", "!")fmt.Printf("去掉!后的数据:%v \n", str)//将字符串左边指定的字符去掉: strings.TrimLeft(“! hello! “,” !”) // [“hello”]//将左边!和”“去掉str = strings.TrimLeft("! hello, word !", "!")fmt.Printf("去掉左边!后的数据:%v \n", str)//将字符串右边指定的字符去掉: strings.TrimRight(“! hello! “,” !”) // [“hello”]//将右边!和””去掉str = strings.TrimRight("! hello, word !", "!")fmt.Printf("去掉右边!后的数据:%v \n", str)//判断字符串是否以指定的字符串开头: strings.HasPrefix(“ftp://192.168.10.1" ,”ftp”) // truehasPrefix := strings.HasPrefix("! hello, word !", "!")fmt.Printf("是否以!开头:%v \n", hasPrefix)//判断字符串是否以指定的字符串结束: strings.HasSuffix(“‘NLT_abc.jpg”,”abc”) //falsehasSuffix := strings.HasSuffix("! hello, word !", "!")fmt.Printf("是否以!结束:%v \n", hasSuffix)//判断字符串是否包含一个字符串hasContains := strings.Contains("hello word", "hello")fmt.Printf("字符串hello word是否包含hello:%v \n", hasContains)}
运行结果
str长度:10
遍历字符串:0
遍历字符串:1
遍历字符串:2
遍历字符串:3
遍历字符串:4
遍历字符串:5
遍历字符串:6
遍历字符串:7
遍历字符串:8
遍历字符串:9
字符串转整数,number_1:12
字符串转整数,number_2:0, 错误信息:strconv.Atoi: parsing "test": invalid syntax
整数转字符串,str:12345
整数转字符串,str:999
字符串转byte数组,str:[104 101 108 108 111 32 119 111 114 100]
byte转字符串,str:abc
数字转2进制字符串,str:1111011
数字转8进制字符串,str:173
数字转10进制字符串,str:123
数字转16进制字符串,str:7b
是否包含字符串foo,isContains:true
字符串含有2个l:%!v(MISSING)
字符串不区分大小写比较是否相等:true
字符串区分大小写比较是否相等:false
字符子串在字符串中第一次出现的地方:2
字符子串在字符串中最后一次出现的地方:13
替换后的字符串:hell-o word hell-o word
分割后的数据:[Mr Li 20 北京朝阳区]
转换成小写后的数据:hello word
转换成大写后的数据:HELLO WORD
去掉空格后的数据:hello, word
去掉!后的数据: hello, word
去掉左边!后的数据: hello, word !
去掉右边!后的数据:! hello, word
是否以!开头:true
是否以!结束:true
字符串hello word是否包含hello:true
参考中文文档time包相关:https://studygolang.com/static/pkgdoc/pkg/time.htm
代码示例
package mainimport ("fmt""strconv""strings""time""unsafe"
)func main() {println("-----常用日期函数测试----")//返回当前系统时间currentTime := time.Now()fmt.Printf("当前系统时间=%v \n", currentTime)//返回当前系统时间年、月、日、日、时、分、秒、毫秒date, month, day := currentTime.Date()fmt.Printf("当前系统年=%v,月=%v,日=%v \n", date, month, day)currentTimeYear := currentTime.Year()currentTimeDay := currentTime.Day()currentTimeMonth := currentTime.Month()currentTimeHour := currentTime.Hour()currentTimeMinute := currentTime.Minute()currentTimeSecond := currentTime.Second()//时间戳currentTimeUnixMilli := currentTime.UnixMilli()//纳秒:常用于生成随机数字(如生成订单号、随机序列等)currentTimeUnixNano := currentTime.UnixNano()//方法【Unix】将t表示为Unix时间,即从时间点January 1, 1970 UTC到时间点t所经过的时间(单位秒)currentTimeUnix := currentTime.Unix()fmt.Printf("当前系统年=%v,月=%v,日=%v,时=%v,分=%v,秒=%v,当前系统毫秒数=%v,当前系统纳秒数=%v,Unix时间=%v \n", currentTimeYear, currentTimeDay, currentTimeMonth,currentTimeHour, currentTimeMinute, currentTimeSecond, currentTimeUnixMilli, currentTimeUnixNano, currentTimeUnix)//Duration 类型用于表示两个时刻 ( Time ) 之间经过的时间,以 纳秒 ( ns ) 为单位。 点击进去可以看到是time里面的自定义类型:type Duration int64start_time := time.Now()// 空循环 uint32 的最大值次数const UINT32_MAX uint32 = ^uint32(0)var i uint32for i = 0; i < UINT32_MAX; i += 1 {}end_time := time.Now()spand_time := time.Duration(end_time.Sub(start_time))fmt.Printf("空循环 uint32 的最大值次数耗时时间(s):%v \n", spand_time.Seconds())println("------时间格式化-----")var now = time.Now()// 以下的数字都是固定的值,不能更换,据说2006/01/02 15:04:05是创始人思考创建go的时间fmt.Println(now.Format("2006")) // 2022fmt.Println(now.Format("01")) // 04fmt.Println(now.Format("02")) // 30fmt.Println(now.Format("15")) // 10fmt.Println(now.Format("04")) // 52fmt.Println(now.Format("05")) // 16// 数字之外的其它字符可以更换fmt.Println(now.Format("2006/01/02 15:04:05")) // 2022/04/30 10:52:16fmt.Println(now.Format("2006-01-02 15:04:05")) // 2022-04-30 10:52:16println("------sleep练习-----")sleep_start_time := time.Now()var num = 1for {fmt.Printf("%v ", num)//休眠30mstime.Sleep(time.Millisecond * 30)if num == 5 {println()break}num++}sleep_end_time := time.Now()sleep_spand_time := sleep_end_time.Sub(sleep_start_time)fmt.Printf("sleep 测试耗费时间(ms):%v \n", sleep_spand_time.Milliseconds())println("------时间戳 <-互转-> 日期字符串-----")// 时间戳转换年月日时分秒(一个参数是秒,另一个参数是纳秒)//Unix返回与给定Unix时间相对应的本地时间,//秒和纳秒。var time_1 = time.Unix(1595289901, 0)var timeStr = time_1.Format("2006-01-02 15:04:05")fmt.Println("时间戳转时间字符串结果:%v \n", timeStr)// 日期字符串转换成时间戳var timeStr2 = "2022-11-25 14:44:52"var tmp = "2006-01-02 15:04:05" //转换模版timeObj5, _ := time.ParseInLocation(tmp, timeStr2, time.Local)fmt.Println("日期字符串转换成ms时间戳结果:%v \n", timeObj5)}
运行结果
-----常用日期函数测试----
当前系统时间=2022-11-25 14:45:58.212786 +0800 CST m=+0.000256501
当前系统年=2022,月=November,日=25
当前系统年=2022,月=25,日=November,时=14,分=45,秒=58,当前系统毫秒数=1669358758212,当前系统纳秒数=1669358758212786000,Unix时间=1669358758
空循环 uint32 的最大值次数耗时时间(s):1.362262833
------时间格式化-----
2022
11
25
14
45
59
2022/11/25 14:45:59
2022-11-25 14:45:59
------sleep练习-----
1 2 3 4 5
sleep 测试耗费时间(ms):154
------时间戳 <-互转-> 日期字符串-----
时间戳转时间字符串结果:%v 2020-07-21 08:05:01
日期字符串转换成ms时间戳结果:%v 2022-11-25 14:44:52 +0800 CST
管道(channel)为引用类型,必须先初始化才能使用;本质是一个队列,有类型,而且线程安全.
管道的定义: var 变量名 chan 数据类型
代码示例
package mainimport ("fmt""math/rand""time"
)func main() {println("------管道测试-------")//简单用法simpleChannel := make(chan int, 2)simpleChannel <- 100simpleChannel <- 200//取出管道数据丢弃<-simpleChannelchannelInfo := <-simpleChannelfmt.Printf("读取simpleChannel管道数据: %v \n", channelInfo)intsChannel := make(chan int, 50)//协程相关在后续进阶篇中介绍,相当于开启一个新的线程执行逻辑,不阻塞main()线程的逻辑执行//开启协程,用于写数据go func() {for true {writeNum := rand.Intn(100)intsChannel <- writeNumfmt.Printf("写入管道数据: %v \n", writeNum)time.Sleep(time.Millisecond * 500)}}()//开启协程,用于读数据go func() {for true {//读取管道数据readInfo := <-intsChannelfmt.Printf("读取管道数据: %v \n", readInfo)time.Sleep(time.Millisecond * 500)}}()//防止数据还没有在协程里打印,main函数退出,main()执行结束后其他相关的协程也会结束time.Sleep(time.Second * 3)//程序结束
}
运行结果
------管道测试-------
读取simpleChannel管道数据: 200
写入管道数据: 81
读取管道数据: 81
写入管道数据: 87
读取管道数据: 87
写入管道数据: 47
读取管道数据: 47
写入管道数据: 59
读取管道数据: 59
写入管道数据: 81
读取管道数据: 81
写入管道数据: 18
读取管道数据: 18
学海无涯,吾生也有涯,而知也无涯,以有涯随无涯. 加油👍
后续还会加入进阶篇,有兴趣的点个关注吧