极速Go语言入门(超全超详细)-基础篇2

简介: 极速Go语言入门(超全超详细)-基础篇2

文章目录


函数进阶

结构体

接口

继承

type

值类型与引用类型

值传递、引用传递

打包、引用包

工具类打包文件代码

引用包代码

方法

异常捕捉处理

字符串常用函数

日期常用函数

管道(channel)


书接上篇:极速Go语言入门(超全超详细)-基础篇


整个基础篇合计32000字左右,如有遗漏可以私信我进行补充


函数进阶


以下展示函数return的使用、普通函数、匿名函数、闭包、defer延迟执行、函数作为参数传递、函数作为函数返回值、内置函数


go内置函数可参考: 中文官方文档中**builtin**(点击跳转)一栏


示例代码

package main
import (
  "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 := 10
  n2 := 29
  n1, 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/123762669
  deferTest("字符参数")
  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 = 29
  fmt.Println("*p = ", *p)
  //引用类型的用make
  stringStringMap := 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 + n2
  return 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 + n2
  return
}
// //普通函数//
// 单个入参、单个出参
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) + value
  return number, resultStr
}
// 多个入参、单个出参
func testFuntionV3(args ...int) (int, string, float64) { //(args ...int)表示可以传递多个参数,可以理解为0到多个参数
  resultNum := 0
  for arg := range args {
    fmt.Println("打印参数arg = ", arg)
    resultNum += arg
  }
  resultStr := "返回值2"
  resultFloatNum := 888.88
  fmt.Printf("最终返回值1=%v, 最终返回值2=%v, 最终返回值3=%v \n", resultNum, resultStr, resultFloatNum)
  return resultNum, resultStr, resultFloatNum
}
// init函数,最大的作用是用来初始化源文件,该函数会在main函数执行前被调用
func init() {
  //do something
  fmt.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 = 10
  return func(x int) int {
    //每一次调用都会给n进行累加赋值,n的值在内存中伴随整个闭包实例的整个生命周期
    n = n + x
    return 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 {
    _ string
    a int
}

示例代码

package main
import (
  "fmt"
)
type UserInfo struct {
  //用户姓名
  name string
  //年龄、身高
  age, height int16 //相同类型可以定义在一行
  //用户地址
  address string
}
// 如果里面的字段类型都一样可以使用这种方法
type UserInfoV2 struct{ name, address string }
// 组合型
type UserInfoV3 struct {
  user_1 UserInfo
  user_2 UserInfoV2
}
func main() {
  //初始化结构体
  info := UserInfo{age: 18, name: "小明", height: 180, address: "浙江省杭州市"}
  fmt.Printf("结构体信息:%v  \n", info)
  //访问结构体属性,info.xxx
  fmt.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 main
import "fmt"
func main() {
  //根据不同的实例调用对应的实例方法
  doVoice(Dark{name: "小鸭子"})
  doVoice(new(Cat))
}
// 定义一个接口
type Animal interface { // type 接口名 interface
  voice()
}
// 定义结构体
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 main
import (
  "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用于类型定义(type definition)与类型别名(type alias),可以理解就是定义一个类型或者给一个类型起别名

示例代码

package main
import (
  "fmt"
  "unsafe"
)
// 定义类型
type UserMsg struct {
  name, phone string
}
// 类型起别名
type myIntType int64
//结构体起别名
type MyUserMsg UserMsg
func main() {
  //使用起了别名的类型
  var number_test myIntType = 999
  fmt.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 main
import (
  "fmt"
  "unsafe"
)
func main() {
  //值传递测试
  var number int64 = 999
  testV1(number)
  //从这里看出调用函数赋值后并不会改变原值
  fmt.Println("main()当前number=", number)
  //应用传递测试
  var i int = 10
  fmt.Printf("i当前的值 = %v \n", i)
  //1、ptr是一个指针变量 2、ptr存的是i变量的地址 3、类型是*int
  var ptr *int = &i
  //赋予新值
  *ptr = 20
  fmt.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 = 1000
  fmt.Println("testV1()当前number=", number)
}

运行结果

testV1()当前number= 1000
main()当前number= 999
i当前的值 = 10 
指针存储的值:0x14000114018, 类型:*int, 占内存字节数:8, 指针存储地址指向的值:20, 指针的地址:0x14000100020 
i修改后的值 = 20 


打包、引用包


有时候我们除了引用go提供的官方包,也可能需要我们自己打包后在其他模块引用封装好的包

如我当前项目文件名为:MyTest

在项目文件路径下创建了packageUtils文件夹,在文件夹下创建了UtilsPKG.go文件

图示如下:

1.png

工具类打包文件代码

package packageUtils
func NumberUtils(number_1 int64, number_2 int64, operation string) (resultNumber float64) {
  switch operation {
  case "*":
    resultNumber = float64(number_2 * number_1)
    break
  case "/":
    resultNumber = float64(number_2 / number_1)
    break
  case "*+":
    resultNumber = float64(number_2 + number_1)
    break
  case "-":
    resultNumber = float64(number_2 - number_1)
    break
  }
  return
}

引用包代码

在其他路径下随意创建一个文件

package main
import (
  "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 main
import (
  "fmt"
  "strconv"
  "unsafe"
)
func main() {
  var number_test MyInt = 888
  result_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 int64
func (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 string
  Age  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 main
import (
  "fmt"
  "strconv"
  "strings"
  "unsafe"
)
func main() {
  //测试异常捕捉处理
  testExceptionCapture();
  fmt.Println("异常捕捉后继续处理流程")
  //测试异常panic
  testPanic();
  //上面函数执行报错后如果没有异常捕捉处理程序直接结束运行
  fmt.Println("异常捕捉处理测试")
}
func testPanic() {
  n1 := 1
  n2 := 0
  n3 := n1 / n2
  //发送异常之后,下面的输出语句不会输出,程序直接结束
  fmt.Println("res:", n3)
}
func testExceptionCapture() {
  defer func() {
    if err := recover(); err != nil {
      fmt.Println("捕获到的异常信息: ", err)
      //异常之后做一些事情
      fmt.Println("发送钉钉告警或者邮件告警等")
    }
  }()
  n1 := 1
  n2 := 0
  n3 := n1 / n2
  //发送异常之后,下面的输出语句不会输出
  fmt.Println("res:", n3)
}

运行结果

捕获到的异常信息:  runtime error: integer divide by zero
发送钉钉告警或者邮件告警等
异常捕捉后继续处理流程
panic: runtime error: integer divide by zero
goroutine 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 main
import (
  "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,16S
  str = 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”) //true
  isContains := strings.Contains("food foo eat", "foo")
  fmt.Printf("是否包含字符串foo,isContains:%v \n", isContains)
  //统计一个字符串有几个指定的子串:strings.Count(“ceheese”, “e”) //4
  countNum := strings.Count("hello word", "l")
  fmt.Printf("字符串含有%d个l:%v \n", countNum)
  //不区分大小写的字符串比较(== 是区分字母大小写的): fmt.PrintIn(strings.EqualFold(“abc”, “Abc”) // true
  isEqual := strings.EqualFold("abc", "Abc")
  fmt.Printf("字符串不区分大小写比较是否相等:%v \n", isEqual)
  fmt.Printf("字符串区分大小写比较是否相等:%v \n", "abc" == "Abc")
  //返回子串在字符串第一次出现的index值,如果没有返回-1 : strings.Index(“NLT_abc”,”abc”) //4
  firstIndex := 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”) //GO
  str = 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”) // true
  hasPrefix := strings.HasPrefix("! hello, word !", "!")
  fmt.Printf("是否以!开头:%v \n", hasPrefix)
  //判断字符串是否以指定的字符串结束: strings.HasSuffix(“‘NLT_abc.jpg”,”abc”) //false
  hasSuffix := 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 main
import (
  "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 int64
  start_time := time.Now()
  // 空循环 uint32 的最大值次数
  const UINT32_MAX uint32 = ^uint32(0)
  var i uint32
  for 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")) // 2022
  fmt.Println(now.Format("01"))   // 04
  fmt.Println(now.Format("02"))   // 30
  fmt.Println(now.Format("15"))   // 10
  fmt.Println(now.Format("04"))   // 52
  fmt.Println(now.Format("05"))   // 16
  // 数字之外的其它字符可以更换
  fmt.Println(now.Format("2006/01/02 15:04:05")) // 2022/04/30 10:52:16
  fmt.Println(now.Format("2006-01-02 15:04:05")) // 2022-04-30 10:52:16
  println("------sleep练习-----")
  sleep_start_time := time.Now()
  var num = 1
  for {
    fmt.Printf("%v ", num)
    //休眠30ms
    time.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)
}


管道(channel)


管道(channel)为引用类型,必须先初始化才能使用;本质是一个队列,有类型,而且线程安全.

管道的定义: var 变量名 chan 数据类型

代码示例

package main
import (
  "fmt"
  "math/rand"
  "time"
)
func main() {
  println("------管道测试-------")
  //简单用法
  simpleChannel := make(chan int, 2)
  simpleChannel <- 100
  simpleChannel <- 200
  //取出管道数据丢弃
  <-simpleChannel
  channelInfo := <-simpleChannel
  fmt.Printf("读取simpleChannel管道数据: %v \n", channelInfo)
  intsChannel := make(chan int, 50)
  //协程相关在后续进阶篇中介绍,相当于开启一个新的线程执行逻辑,不阻塞main()线程的逻辑执行
  //开启协程,用于写数据
  go func() {
    for true {
      writeNum := rand.Intn(100)
      intsChannel <- writeNum
      fmt.Printf("写入管道数据: %v \n", writeNum)
      time.Sleep(time.Millisecond * 500)
    }
  }()
  //开启协程,用于读数据
  go func() {
    for true {
      //读取管道数据
      readInfo := <-intsChannel
      fmt.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 

学海无涯,吾生也有涯,而知也无涯,以有涯随无涯. 加油👍

后续还会加入进阶篇,有兴趣的点个关注吧

相关文章
|
17天前
|
存储 JSON 监控
Viper,一个Go语言配置管理神器!
Viper 是一个功能强大的 Go 语言配置管理库,支持从多种来源读取配置,包括文件、环境变量、远程配置中心等。本文详细介绍了 Viper 的核心特性和使用方法,包括从本地 YAML 文件和 Consul 远程配置中心读取配置的示例。Viper 的多来源配置、动态配置和轻松集成特性使其成为管理复杂应用配置的理想选择。
38 2
|
15天前
|
Go 索引
go语言中的循环语句
【11月更文挑战第4天】
26 2
|
15天前
|
Go C++
go语言中的条件语句
【11月更文挑战第4天】
30 2
|
1天前
|
存储 Go 索引
go语言使用for循环遍历
go语言使用for循环遍历
15 7
|
4天前
|
存储 Go
go语言 遍历映射(map)
go语言 遍历映射(map)
18 2
|
5天前
|
Go 调度 开发者
Go语言中的并发编程:深入理解goroutines和channels####
本文旨在探讨Go语言中并发编程的核心概念——goroutines和channels。通过分析它们的工作原理、使用场景以及最佳实践,帮助开发者更好地理解和运用这两种强大的工具来构建高效、可扩展的应用程序。文章还将涵盖一些常见的陷阱和解决方案,以确保在实际应用中能够避免潜在的问题。 ####
|
5天前
|
测试技术 Go 索引
go语言使用 range 关键字遍历
go语言使用 range 关键字遍历
14 3
|
5天前
|
测试技术 Go 索引
go语言通过 for 循环遍历
go语言通过 for 循环遍历
16 3
|
7天前
|
安全 Go 数据处理
Go语言中的并发编程:掌握goroutine和channel的艺术####
本文深入探讨了Go语言在并发编程领域的核心概念——goroutine与channel。不同于传统的单线程执行模式,Go通过轻量级的goroutine实现了高效的并发处理,而channel作为goroutines之间通信的桥梁,确保了数据传递的安全性与高效性。文章首先简述了goroutine的基本特性及其创建方法,随后详细解析了channel的类型、操作以及它们如何协同工作以构建健壮的并发应用。此外,还介绍了select语句在多路复用中的应用,以及如何利用WaitGroup等待一组goroutine完成。最后,通过一个实际案例展示了如何在Go中设计并实现一个简单的并发程序,旨在帮助读者理解并掌
|
6天前
|
Go 索引
go语言按字符(Rune)遍历
go语言按字符(Rune)遍历
21 3