Go的进阶学习

(1)、字符串的操作

  • 1.1、strings包的使用

    • 1.1.1、字符串的首尾字符的判断

    • strings.HasPrefix(s string, prefix string) bool :判断字符串s是否以prefix开头

    • strings.HasSuffix(s string, suffix string) bool :判断字符串s是否以suffix结尾

    范例:自动在输入的url前面加上 “http://” 末尾加上 “/”

    package main
    
    import (
        "fmt"
        "strings"
        // "strconv"
    )
    
    func main(){
        var n string
        fmt.Println("Input Your Web Urls:")
        fmt.Scanf("%s",&n)
        n=http_ch(n)
        n=http_ch_end(n)
        fmt.Printf("Your url is ========> [%s]",n)
    }
    
    //自动在web的URL前面添加 "http://""
    func http_ch(url string) string {
        if strings.HasPrefix(url,"http://"){
            return url
        } else {
            return fmt.Sprintf("http://%s",url)
        }
    }
    
    //自动在web的URL末尾添加 "/"
    func http_ch_end(url string) string {
        if strings.HasSuffix(url,"/"){
            return url
        } else {
            return fmt.Sprintf("%s/",url)
        }
    }
    • 1.1.2、其他字符串的判断

    • 1.strings.Index(s string, str string) int

      判断str在s中首次出现的位置,如果没有出现,则返回-1

    • 2.strings.LastIndex(s string, str string) int

      判断str在s中最后出现的位置,如果没有出现,则返回-1

    • 3.==strings.Replace(str string, old string, new string, n int)==

      字符串替换

      str_a="hello wid"
      result:=strings.Replace(str_a,"i","or")

      执行后,result的值为hello word

    • 4.strings.Count(str string, substr string)int

      计算substr字符在字符串str中出现的次数

    • 5.strings.Repeat(str string, count int)string

      将str字符串重复count次

    • 6.strings.ToLower(str string)string

      将字符串全部转为小写

    • 7.strings.ToUpper(str string)string

      将字符串全部转为大写

    • 1.1.3、去掉字符串指定字符

    • 1.strings.TrimSpace(str string)

      去掉字符串首尾空白字符(类似python的.strip()方法)

    • 2.strings.Trim(str string, cut string)

      去掉字符串首尾cut字符

    • 3.strings.TrimLeft(str string, cut string)

      去掉字符串首cut字符

    • 4.strings.TrimRight(str string, cut string)

      去掉字符串尾cut字符

    • 1.1.4、字符串的切割

    • 1.strings.Field(str string)

      返回str空格分隔的所有子串的slice(类似python的.split()方法)

      strings.Field('jk iu 123') ==结果为>>>> ["jk","iu","123"]

    • 2.strings.Split(str string, split string)

      返回str split分隔的所有子串的slice

      strings.Split('jk#iu#123','#') ==结果为>>>> ["jk","iu","123"]

      第一种Field方法相当于:strings.Split(str string, ” “)

    • 1.1.5、字符串的拼接

    • strings.Join(s1 []string, sep string)

      用sep把s1中的所有元素链接起来

  • 1.2、strconv包的使用

    • strconv.Itoa(i int)

    把一个整数i转成字符串

    • strconv.Atoi(str string)(int, error)

    把一个字符串转成整数

    var str_b string = "1980"
    
    num,err := strconv.Atoi(str_b)
    
    if err !=nil {  //输入错误时,执行这步。比如:当str_b为"1980x"时会执行这步
    
    fmt.Printf("你应该输入一个整数类似的字符串,%s",err)
    }
    fmt.Println(num)

(2)、数据类型

  • 2.1、时间类型

    时间类型在time包中,ime.Time类型,用来表示时间。

    • #### 2.1.1、如何获取当前时间?

    now := time.Now()

    now输出为 2018-08-21 17:32:23.5956001 +0800 CST m=+0.012000001一个完整的时间格式,很明显我们用不了这么多的数据

    • #### 2.1.2、时间的格式化

    方法一 : 使用 time.Now().Day(),time.Now().Minute(),time.Now().Month(),time.Now().Year() 这些方法,别可以输入当前的 日、分、月、年,然后可以使用printf格式化的输出:

    例如:

    package main
    
    import (
        "time"
        "fmt"
    )
    
    func main() {
    
        fmt.Printf("%d-%02d-%02d",time.Now().Year(),time.Now().Month(),time.Now().Day()) //%02d表示至少使用2个位数表示
    
    }

    输出结果为:

    2018-08-19

    方法二 :* 使用time.Now()自带的Format方法,Format里面是时间格式显示模板,并且模板内的年必须是2006、月必需是01、日必需是02、时必需是15(或3)、分必需是04。模板展示的效果才生效。然后根据Format模板
    里面的格式来显示日期

    不要问我模板内展示的日期为什么必需要用 2006年01月02日15时04分,据说是go语言诞生的时间,我可以吐槽下有点自恋吗 -.-!

    time.Now().Format(“2006-01-02”) : 表示使用 “年-月-日” 的方式来显示,显示结果为 2018-08-19
    time.Now().Format(“20060102”) : 表示使用 “年月日” 的方式来显示,显示结果为 20180819

    • 2.1.3、时间的常量

    • time.Duration 用来表示纳秒

    • time.second 用来表示秒

    • 其他常量:

      const (
      Nanosecond  Duration = 1                   //纳秒
      Microsecond          = 1000 * Nanosecond   //微秒
      Millisecond          = 1000 * Microsecond  //毫秒
      Second               = 1000 * Millisecond  //秒
      Minute               = 60 * Second         //分
      Hour                 = 60 * Minute         //时
  • 2.2、指针类型

    和C语言里的指针一样,指针存储了变量的内存地址并指向变量

    • #### 2.2.1、指针的声明

    var <指针名称> [指针类型]*

    指针类型表示指针要指向什么类型的变量。范例:

    var p1 *int //一个执行整数类型的指针
    var p2 *string //一个执行字符串类型的指针
    • #### 2.2.1、指针的赋值

    指针存储的是变量的地址,因此应该把变量的内存地址赋给指针,

    如何获取变量的内存地址

    和C语言里面一样,使用 “&” 这个取址符号来取变量的地址,比如取a变量的内存地址就是>>> “&a”

    指针的赋值:

    var p1 *int
    
    int_a :=10
    
    p1 = &int_a

    指针执行变量的值

    指针存储了变量的内存地址,使用 “*” 号可以将指针指向变量的值,比如上面的赋值范例,使用 *p1 就表示执行变量int_a的值,就是10。

    var p1 *int
    
    int_a :=10
    
    p1 = &int_a
    
    fmt.Println(*p1)

    结果为

    10

    指针传入函数

    func main() {
    
        var p1 *int
        int_a :=10
        p1 = &int_a
    
        mac(p1)
    
        fmt.Println(int_a)
    }
    
    func mac(p *int){
    
        *p = 20
        return
        }

    执行结果为

    20

    请注意指针传入函数时,对指针指向的值的改变也会应用到变量中,此时指针指向的值在函数中已经不是局部变量了

(3)、流程控制

  • 3.1、if判断

    格式1,简单的判断:

    if <条件> {

    …执行语言

    }

    格式2,带else的判断:

    if <条件> {

    …执行语言

    } else {

    …执行语句
    }

    格式3,带else if的的判断:

    if <条件> {

    …执行语言

    } else if <条件> {

    …执行语句
    } else if <条件> {

    …执行语句

    } else {

    …执行语句
    }

    else if可以写很多个

  • 3.2、switch判断

    if判断中的else if饱受诟病,当然switch就是为解决此来的。switch大体上分为2种写法:

    需要注意除非有关键字 fallthrough ,否则在执行一个满足的case条件的语句后,整个语句结束

    写法1范例:

    var i=0
    
    switch i {
    case 0:
      fmt.Println("equa 0")
      //fallthrough  表示向下执行一个case条件内的语句
    case 1:  
      fmt.Println("equa 1")
    case 3,4,5:  
      fmt.Println("equa 3 or 4 or 5")        
    default: 
      fmt.Println("equa 0")
    }

    switch后面有变量,case后面只能是值的形式

    写法2范例:

    var i=0
    
    switch {
    case i==0:   
       fmt.Println("equa 0")     
    case i>0:  
      fmt.Println("more than 0")
    case i<0 && i !=-5//可以使用逻辑与/或表示多条件
      fmt.Println("less than 0 but not equa -5")   
    default: 
      fmt.Println("equa 0") 
    }

    switch后面无变量,case后面是条件判断的形式

  • 3.3、for循环

    • #### 3.3.1、for循环的写法

    go的for循环和c语言的十分类似,只是没有小括号,并且需要注意下变量的赋值(以 := 的方式赋值)

    格式: for 初始化语句; 条件判断; 变量修改 { 循环语句块 }

    范例:

    //输出从1到100的整数
    for i:=1;i<=100;i++ { //i++相当于i=i+1
    fmt.Println(i)
    }

    for循环写法二 for 条件判断 {}

    此格式多用于无限循环(不是死循环,内部你得有个break跳出循环的关键字和触发此关键字的条件)情况:

    //输出从1到100的整数
    var i=1
    for {
    
    fmt.Println(i)
    
    i=i+1
    
    if i>100{
     break
     }
    }
    • #### 3.3.2、for循环对数组、slice、map、chan的遍历

    for循环配合range关键字能实现对数组、slice、map、chan的遍历,使用范例:

    var str1="abcde" //字符串是特殊的数组
    for i,j:=range str1{  //i表示下标;j表示元素,这里是字符
        fmt.Println(i,string(j)) //string将字符转为字符串,否则是ACII码的形式
    • #### 3.3.3、for循环中的流程控制

    break、continue

    break表示终止整个循环。continue表示停止本次循环,继续下一次的循环

    for i:=0;i<100;i++{
    
    if i == 25 {   //i值为25时,停止本次循环,从26开始继续循环
        continue
        }
    
    if i > 50{    //i值大于50时,终止循环
        break
        }
    fmt.Println(i)
    }

    上面范例执行后,只输出0到50的整数,除了25

    goto 和 label 语句

    label是一个大写的英文标识符,通常和goto、continue关键字组合使用来控制程序的流程

    func main() {
      //continue 到label
      LABEL1:
      for i := 0; i <= 5; i++ {
        for j := 0; j <= 5; j++ {
            if j == 4 {
                continue LABEL1  ////回到 LABEL1这个lable,然后向下执行
            }
            fmt.Printf("i is: %d, and j is: %d\n", i, j)
        }
      }
    
      //goto到label
      i := 0
    
      HERE:
      print(i)
      i++
      if i == 5 {
        return
      }
      goto HERE  //回到HERE这个lable,然后向下执行。
    }

(4)、函数

go中的函数不支持重载,一个包不能有两个名字一样的函数。并且和python一样,函数可以赋给变量: func def1(){...} ... c:=def1 函数def1只是表示了一个内存地址。

  • 4.1、函数的声明

    声明语法: ==func 函数名 (参数列表) [(返回值列表)] { …函数体… }==

    (返回值列表)是写上返回值的类型(也可以写上形式返回值),函数的参数可以有多个,返回值也一样可以有多个。函数的声明范例如下:

    func add() { //简单的函数声明
    ...
    }
    
    func add(a int,b int) { //带参数的函数声明
    ...
    }
    
    func add(a int,b int) int { //带2个整数参数、带1个整数返回值的函数声明
    ...
    }  
    
    func add(a ,b int) (int,int){ //带2个整数参数(参数类型相同时,可以简写)、带2个整数返回值的函数声明
    ...
    }
    
    func add(a ,b int,c string) str1 sting { //带2个整数参数1个字符串参数,带一个字符串返回值的函数声明。并且函数体中可以将返回值赋予str1
    ...
    }
  • 4.2、函数的值参数传递和引用参数传递

    无论是值传递,还是引用传递,传递给函数的都是变量的副本,不过,值传递是值的拷贝。引用传递是地址的拷贝,一般来说,地址拷贝更为高效。而值拷贝取决于拷贝的对象大小,对象越大,则性能
    越低。

    关于值参数传递和引用参数传递的范例:

    package main
    
    import "fmt"
    
    func modify(a int) {  //值传递
      a = 100
    }
    
    
    func modify_ar(a *int) {  //引用传递,传入的是指针
      *a = 100              //修改指针指向的地址内的值
    }
    
    func main() {
      a := 8
      fmt.Println(a)     //值为8
      modify(a)          //因为是值传递,函数内为局部变量,因此不会修改全局a变量的值
      fmt.Println(a)     //值仍然为8
    
    
      modify_ar(&a)      //引用传递,传入的是变量的内存地址,函数体内如果对指向地址内的值修改,则被传入地址的变量值也会改变
      fmt.Println(a)     //值变为了100
    } 
  • 4.3、go函数的“高级函数”

    go函数的形式参数可以为一个函数,需要指定“函数参数”的类型,如 函数的传入参数类型和传出参数类型,只需要类型(int、float32、string)即可

    “函数参数”的格式: ==func (参数类型) 返回参数类型==

    范例:

    func oper (a func(int,int) int,b int,c int) int {  //此函数形式参数a的类型为函数类型
      return a(b,c)
    }
    

    上面范例只要将符合的传入函数传入oper就可以了,传入函数只要有2个整形参数,并且返回一个整数类型的值就符合要求,比如下面的函数就可以作为传入函数:

    func add(a,b int) int {
      return a+b
    }
    
    result:=oper(add,10,20)

    使用type自定义数据类型

    函数的传入参数为函数时,使用类似“func(int,int) int”的格式表示晓得比较复杂,如果需要的传入函数比较复杂的话,可能要写很长的函数格式,因此可以使用 “type” 关键字来定义一个“函数 ”数据类型
    (因为函数也是一种数据类型):

    type my_def func(int,int) int //定义一个函数数据类型
    
    func oper (a my_def,b int,c int) int {  //此函数形式参数a的类型不是系统自带的类型,而是自定义的my_def类型,而my_def是一个函数数据类型
      return a(b,c)
    }
  • 4.4、go函数中的“defer”延缓

    go函数体中,有“defer”关键字,关键字后面跟的操作将会暂停执行,在接下来的函数体内其他操作结束后,再来操作defer关键字后面的操作。

    func example(a,b int) {
    
      defer fmt.Printf("%d",a+b)  //遇到defer就暂停后面的操作,等待下面语句的操作完成后再来操作
    
      fmt.Printf("%d+%d=",a,b)
    }
    func main(){
      example(10,20)
    }

    执行结果:

    10+20=30

    上面的范例中,函数在执行时,遇到了defer 关键字,因此 “fmt.Printf(“%d”,a+b)” 暂时不执行,而往下执行下面的“fmt.Printf(“%d+%d=”,a,b)”,执行完后,函数体内没有其他的操作了,再返回去执行defer后面的“defer fmt.Printf(“%d”,a+b)”

    如果函数中有多个“defer”关键字,则 最先使用 “defer” 的最后执行

    defer的用途

    defer使得函数具有延缓执行的特性,非常适合添加在那些程序结束后必须要进行的步骤,比如打开一个文件操作后在最后必须关闭句柄。这在具有多个流程判断的程序中非常好用

    //关闭文件句柄
    func file_oper(){
      file := open(c:\log.txt) 
    
      defer file.Close()
    
      if ... {
      ...
      return 
      }
      ...
    }
    
    //锁资源释放
    func read() {
     mc.Lock()
     defer mc.Unlock()
     //其他操作
    }
    
    //数据库连接释放
    func read() {
      conn := openDatabase()
      defer conn.Close()
      //其他操作
    }
  • 4.5、函数的可变参数

    go函数参数可以允许输入任意多个参数,参数的个数是动态的可变的。在函数定义时,形式参数使用“arg…<参数类型>”表示可以传入动态个数的参数,函数体中,传入的参数都存在arg这个数组中。这个和python函数的动态参数
    def function1(*a)比较类似,函数体中,所有传入的参数都在a这个元组内

    可变参数

    var sum int
    func add_args(a int,args...int) int { //第一位置是1个整数类型的参数,后面的参数个数动态随机
      for i:=0;i//如何遍历动态参数
          sum:=sum+agrs[i]
      }
    return a+sum
    }
  • 4.6、匿名函数

    不必声明函数的名称,直接声明然后就调用。被传入的参数写在匿名函数最后

    func main() {
    func(a int,b int)  { //匿名函数,直接打印结果
        fmt.Println(a+b)
    }(10,20) //传入的参数10和20
    }

    执行结果

    30

相关代码

package main

import (
    "fmt"
)

type my_def func(int,int) int

func main() {


    fmt.Println(oper(sub,90,80))

    fmt.Println(args(10,11,12,12,33,40))

    func(a int,b int)  {
        fmt.Println(a+b)
    }(10,20)

    example(10,20)

}

func sub(a,b int) int{
    return a*b
}

func oper(a my_def,b,c int) int {

    return a(b,c)
}


func args(a int,args...int) int {
    var sum int 
    for i:=0;i<len(args);i++{
        sum=sum+args[i]
    }
    return a+sum
}

func example(a,b int) {

    defer fmt.Printf("%d",a+b)  //遇到defer就暂停后面的操作,等待下面语句的操作完成后再来操作

    fmt.Printf("%d+%d=",a,b)
}

执行结果

7200
118
30
10+20=30

你可能感兴趣的:(Go语言)