三、golang时间、流程控、函数

一、本篇内容

1、string和strconv使用

2、go中的时间和日期类型

3、流程控制

4、函数讲解

二、string和strconv使用

1、  string.HasPrefix(s tring ,prefix string)bool,判断字符串s是否是prefix开头

2、  string.HasSuffix(s string,suffix string) bool 判断字符串s是否以suffix结尾

练习1、判断url是否是以http://开头,如果不是则加上http://
a)    练习2、判断一个路径是否以“/”结尾,如果不是,则加上/
练习
package main

import (
   "fmt"
   "strings"
)

func urlprocess(url string) string{
//这里需要返回值的原因是在内部更改之后外部不会变只有返回值才会变,如果不用返回值那就要用指针传递
   result:=strings.HasPrefix(url,"http://")
   if !result{
      url=fmt.Sprintf("http://%s",url) //如果不用返回值,那么这里就要传递指针
   }
   return url
}

func pathprocess(path string) string{
   result:=strings.HasSuffix(path,"/")
   if !result{
      path=fmt.Sprintf("%s/",path)
   }
   return path
}


func main(){
   var (
      url string
      path string
   )

   fmt.Scanf("%s%s",&url,&path)//终端输入
   url=urlprocess(url)
   path=pathprocess(path)

   fmt.Println(url)
   fmt.Println(path)

}
执行:
D:project>go build -o day3/example1.exe go_dev/day3/example/example1

D:project>cd day3

D:projectday3>example1.exe
10.100.8.8/user/login c:/study
http://10.100.8.8/user/login
c:/study/
View Code

1、  strings.Index(s string,str string)int ,判断str在s中首次出现的位置,如果没有出现返回-1

2、  strings.LastIndex(s string,str string) int 判断str在s中最后出现的位置,如果没有出现返回-1

练习3:写一个函数返回一个字符串在另一个字符串的首次出现和最后出现的位置

func strIndex(str string,substr string)(int,int){}

3、  strings.Replace(str string,old string,new string,n int)字符串替换。str是原生的字符串,old要替换的字符串,new是新的字符串,n为替换的次数

如:”hehello” ,“he” ,“rr” ,1   结果为:”rrhello”  如果全部替换,那么n为-1或者0

4、  strings.Count(str string,substr string)int 字符串计数

5、  strings.Repeat(str string,count int)string:重复count次str

6、  strings.ToLower(str string)string 转为小写

7、  string ToUpper(str string)string:转为大写

练习:写一个函数分别演示Replace、Count、Reeat、ToLower、ToUpper的用法

10、strings.TrimSpace(str string):去掉字符串首尾空白字符串

如:strings.TrimSpace(“  sk  ”) 结果为sk

11、strings.Trim(str string,cut string):去掉字符串两边cut字符

如:

         strings.Trim(“abbacba”,”ab”)结果

12、strings.TrimLeft(str string,cur string):去掉字符串首cut字符

13、strings.TrimRight(str string,cut string):去掉字符串末尾cut字符

14、strings.Field(str string):返回str空格分隔的所有字串slice

如:strings.Field(“abc d  e”)  结果为[“abc”,”d”,”e”]

15、strings.Split(str string,split string)返回str split分隔的所有字串slice

如:

strings.Split(“a,b,c”)  [“a”,”b”,”c”]

16、strings.Join(sl [] string,sep string)用sep把sl中的所有元素连接起来

17、strconv.Itoa(i int) 把一个整数i转化为字符串。还可以用fmt的方式格式化转为字符串

18、strings.Atoi(str string)(int,error)把一个字符串转为为整数

package main

import (
   "strings"
   "fmt"
   "strconv"
)

func main(){
   str:="hello world abc 
"
   result:=strings.Replace(str,"world","you",1)
   fmt.Println("replace",result)

   count:=strings.Count(str,"1")
   fmt.Println("count",count)

   result=strings.Repeat(str,3)
   fmt.Println("repeat",result)

   result=strings.ToLower(str)
   fmt.Println("tolower",result)

   result=strings.ToUpper(str)
   fmt.Println("toupper",result)

   result=strings.TrimSpace(str)
   fmt.Println("trimspace",result)

   result=strings.Trim(str,"

")
   fmt.Println("trim",result)

   result=strings.TrimLeft(str,"

")
   fmt.Println("trimleft",result)

   result=strings.TrimRight(str,"

")
   fmt.Println("trimright",result)

   splitResult:=strings.Fields(str)
   for i:=0;i<len(splitResult);i++{
      fmt.Println("fields",splitResult[i])
   }

   splitResult=strings.Split(str,"l")
   for i:=0;i<len(splitResult);i++{
      fmt.Println("split",splitResult[i])
   }


   str2:=strings.Join(splitResult,"l")
   fmt.Println("join",str2)

   str2=strconv.Itoa(1000)
   fmt.Println("itoa",str2)

   number,err:=strconv.Atoi(str2)
   if err!=nil{
      fmt.Println("can not conert to int",err)
   }
   fmt.Println(number)

}
执行结果:
D:project>go build go_dev/day3/example/example2

D:project>example2.exe
replace hello you abc

count 0
repeat hello world abc
hello world abc
hello world abc

tolower hello world abc

toupper HELLO WORLD ABC

trimspace hello world abc
trim hello world abc
trimleft hello world abc

trimright hello world abc
fields hello
fields world
fields abc
split he
split
split o wor
split d abc

join hello world abc

itoa 1000
1000
View Code

三、时间和日期类型

1、  time包

2、  time.Time类型,用来表示时间

3、  获取当前时间,now:=time.Now()

4、  time.Now().Day(),time.Now().Minute(),time.Now().Month(),time.Now().Year()

5、  格式化,fmt.Printf(“%02d/%02d/%02d %02d:%02d%02d”,now.Year()...)

6、  time.Duratime用来表示纳秒

7、  一些常量:

const(
	Nanosecond Duration=1						//纳秒
	Microsecond       =1000*Nanosecond      //微妙
	Milisecond         =1000*Microsecond     //毫秒
	second			  =1000*Milisecond      
	Minute			  =60*second
	Hour			  =60*Minute
)

8、  格式化

now :=time.Now()
fmt.Printf(now.Format(“02/1/2006  15:04”))
fmt.Printf(now.Format(“2006/1/02  15:04”))
fmt.Printf(now.Format(“2006/1/02”))

练习:

写一个程序,获取当前时间,并且格式化为 2017/06/15 08:05:00形式

写一个程序,统计一段代码的耗时操作,单位精确到微妙

package main

import (
   "time"
   "fmt"
)

func test(){
   time.Sleep(time.Millisecond*100)
}



func main(){
   now:=time.Now()
   fmt.Println(now.Format("2006/01/02 15:04:05"))  //获取当前时间
   start:=time.Now().UnixNano()  //获取1970年到现在的纳秒数
   test()
   end:=time.Now().UnixNano()
   fmt.Printf("cost:%d us
",(end-start)/1000)  //统计时间
}
/*
D:project>go build go_dev/day3/example/example3

D:project>example3.exe
2017/11/24 16:49:38
*/
View Code

四、指针类型:

1、  普通类型,变量存的就是值,也叫值类型

2、  获取变量的地址用&,比如:var a int 获取a的地址:&a

3、  指针类型,变量保存的是一个地址,这个地址所指向的才是值

4、  获取指针类型所指向的值,使用:*,比如var *p int 使用*p获取p指向的值

取地址前面加&,获取值前面加上* ,上面图,地址就相当于中介

1、  指针变量怎么去声明

2、  指针赋值应该赋值的是一个地址

3、  用*p这种变量前面加*去取指针所指的值

练习:

写一个程序,获取一个变量的地址,并且打印到终端

package main

import "fmt"

func main(){
   var a int=10
   fmt.Println(&a)

   var p *int
   p=&a
   fmt.Println(*p)
   *p=100
   fmt.Println(a) //这里的a的值为100
   
   var b int=999
   p=&b
   *p=5
   fmt.Println(a) //100
   fmt.Println(b) //5
   
}
View Code

解析:

上面p是一个指针,然后这个指针赋值,指向了a的地址,然后让指针的值等于100,这个时候也就是改变了a的地址,也就是a的值为100

然后下面p这个指针指向了b的地址,然后把指针的值设置为5,这个时候b的值就改变了变成了5,原因是指针就是b的地址,b的值变成了5.但是a的值不变,因为此时指针对应的是b而不是a

巧记:

如果给指针赋值地址,就把指针当作赋值的地址,然后给*p赋值,也就是相当于给这个地址复制

其实指针就是一个地址

写一个函数输入一个int类型的指针,并在函数中修改所指向的值,在main函数中调用这个函数,并把修改前后的值打印到终端,观察结果

package main

import "fmt"

func modify(p * int){
   fmt.Println(p)  //打印的是一个地址
   *p=1000900
   return
}

func main(){
   var a int=10
   fmt.Println(&a)

   var p *int
   p=&a
   fmt.Println(*p)
   *p=100
   fmt.Println(a) //这里的a的值为100
   modify(&a)  //因为参数是指针,所以这里传入的应该是地址
   fmt.Println(a)//1000900
}
上面第一个函数的参数是指针,所以传入参数的时候应该传入的是一个地址(指针就是一个地址),然后在函数中进行给指针赋值,也就是给地址赋值所以a的值就变化了
View Code

流程控制

1、  if/else

注意这里的else必须是上一个语句块的最后位置

写一个程序,从终端输入一个整数,如果不是整数就报错

package main

import (
   "fmt"
   "strconv"
)

func main(){
   var str string
   fmt.Scanf("%s",&str)   //这里是获取输入内容的地址
   number,err:=strconv.Atoi(str)
   if err!=nil{
      fmt.Println("converf failed",err)
      return
   }
   fmt.Println(number)
}
View Code

2、  swich case语句

语法:

         swich var {      //这里第一行是变量

                   case var1:

                   case var2:

                   case var3:

                   default:

}

写法二、多个条件可以写到一行里面

上面的一个case执行完毕之后,这个流程就结束了,如果想要执行下一个case,需要在case下面写上fallthrough

 如:

package main

import "fmt"

func main(){
   var a int=10

   switch a{
   case 0:
      fmt.Print("a is equal 0")
   case 10:
      fmt.Println("a is equal 10")
   default:
      fmt.Println("a is equal default")
      }
}
方法二、
package main

import "fmt"

func main(){
   var a int=10

   switch a{
   case 0,1,2,3,4:
      fmt.Print("a is equal 0")
   case 10:
      fmt.Println("a is equal 10")
   default:
      fmt.Println("a is equal default")
      }
}
switch的条件判断写法
var i =0
switch {
condition1:   //条件语句块
	fmt.Println(“i>0 and i<10”)
condition2:
	fmt.Println(“i>10 and i<20”)
default:
	fmt.Println(“default”)
}
或者
var i=0
switch{
case i>0&&i<10:
	fmt.Println(“i>0 and i<10”)
case i>10&& i<20:
	fmt.Println(“i>10 and i<20”)
default:
	fmt.Println(“default”)
}
或者
switch i:=1;{
	case i>0 &&i<10:
	fmt.Println(“i>0 and i<10”)
	case i>10&& i<20:
	fmt.Println(“i>10 and i<20”)
	default:
	fmt.Println(“default”)
}

练习:猜数字,写一个程序,随机生成一个0到100的整数n,然后用户在终端,输入数字,如果和n相等,则提示用户猜对了,如果不想等,则提示用户,大于或小于n

package main

import(
   "fmt"
   "math/rand"
)

func main(){
   var n int
   n=rand.Intn(100)
   for {
      var input int
      /*
      下面如果不加上
的话会输出两次,原因是输入的数字和回车会放入到缓冲区,然后scanf
      过来取,首先取输入的数字,然后取回车键,分别打印。
      这里的Scanf默认是空格来分割的,这里用
来分割,就不会打印输出两次了
      */
      fmt.Scanf("%d
",&input)
      flag:=false
      switch {
      case input==n:
         fmt.Println("you are right")
         flag=true
      case input>n:
         fmt.Println("bigger")
      case input<n:
         fmt.Println("less")
      }
      if flag{
         break
      }
   }
}
View Code

for循环

for 初始化;条件判断;变量修改

用法1、

for i=0;i<len(a);i++{

}

外层控制行数,内层控制数量

package main

import(
   "fmt"
)

func print(n int){
   for i:=1;i<n+1;i++{
      for j:=0;j<i;j++{
         fmt.Printf("A")
      }
      fmt.Println()  //这里代表换行
   }
}

func main(){
   print(6)
}
View Code

2、

for 条件{

}

如:

for i>0{

fmt.Println(“i>0”)

}

for true{   //无限循环

fmt.Println(“i>0”)

}

for{        //死循环

fmt.Println(“i>0”)

}

3、  for range语句

str :=”hello world ,中国”

for i,v :=range str{   //i是下标。v是下标对应的值。这里的i和v是没有声明的所以需要:=

         fmt.Printf(“index[%d] val[%e] len[%d] ”,i,v,len([]byte(v)))

}

用来遍历 slice、map、chan

4、

str:=”hello world,中国”

for i,v:=range str{

         if i>2{

continue

}

if i>3{

break

}

fmt.Println(“index[%d],val[%c] len[%d]”,i,v,len([] byte(string(v))))//这里因为底层是byte,所以这里把长度转化字符串,然后再转换成字节

}

goto和label   HERE语句

首先定义一个label标识,可以跳到这个标识的位置

如:
package main
import (
    “fmt”
)
func main(){
LABEL:
    for i:=0;i<=5;i++{
for j:=0;j<=5;j++{
    if j==4{
        countinue LABEL   //当j等于4的时候就跳到LABEL上面
}
fmt.Printf(“i is %d j is :%d/n”,i,j)
}
}
}
View Code

goto,只能在一个函数中跳转,实现一个for循环

package main
func main(){
	i:=0
HERE:           //这里的HERE是一个标识
	print(i)
	i++
	if i==5{
	return
}
	goto HERE
}
小练习:
for i:=0;i<7;i++{
	if i%2==0{
	continue
}
fmt.Println(i)
}
结果为1 3 5

 函数

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

func add(){}

func add(a int ,bint){}

func add(a int,b int)int{}

func add(a int,b int)(int,int){}

func add(a,b int)(int,int){}

2、  golang函数特点:

a)         不支持重载,一个包不能有两个名字一样的函数

b)         函数是一等公民,函数也是一种类型,一个函数可以赋值给变量

c)         支持匿名函数

d)         支持多返回值

3、  golang函数的特点

1、package main

import(
   "fmt"
)

func add(a,b int) int{
   return a+b
}

func main(){
   c:=add  //可以赋值给一个变量,这里的c就相当于指针(地址)
   fmt.Println(c)

   sum:=c(10,20)  //因为地址已经赋值,所以这里可以进行运算
   fmt.Println(sum)

}

2、
type是定义一种类型 后面是类型名字,第三个才是一种类型
package main

import (
   "fmt"
)

type add_func func(int,int) int //这个定义一个类型名字是add_func 类型是一个函数
func add(a,b int) int{
   return a+b
}

func operator(op add_func,a int,b int) int{//这里的op是函数类型
   return  a+b
}

func main(){
   var c add_func
	c=add
   fmt.Println(c)
   sum:=operator(c,100,200) //注意这里的c也就是add要和op的类型是一样,不然会出错
   fmt.Println(sum)
}

  

函数参数传递的方式

值传递

引用传递

注意点:

1、无论是值传递还是引用传递,传递给函数的都是变量的副本,不过,值传递是值的拷贝。引用传递是地址的拷贝,一般来说,地址拷贝更为高效,而值拷贝取决于拷贝的对象大小,对象越大,则性能越低  (地址64位最长为8个字节)

2、拷贝如果是一个变量,会拷贝变量所占的内存数量,不会去判断是值类型还是引用类型

3、map,slice,chan、指针、interface默认传递的是引用

4、基本的数据类型都是值传递,结构体也是值传递,如int

下面程序结果
package main
import “fmt”
func modify(a int){
	a=100
}
func main(){
	a:=8
	fmt.Println(a) //8
	modify(a)
	fmt.Println(a)  //8
}
上面为什么结果没变呢?
解析:
原因是这里的a是值传递,然后在modify这个是值传递,然后里面修改的只是副本,如果想修改必须是把值传递变成引用传递

package main

import(
   "fmt"
)

func modify(a *int){
   *a=100
}

func main(){
   a:=8
   fmt.Println(a)
   modify(&a)
   fmt.Println(a)
}

结果:

//8

//100

  

命名返回值的名字

func add(a,b int)(c int){
	c=a+b
	return
}
上面的c可以直接用,然后这样return的时候就不用在return后面写上c了
如:
func calc(a,b int)(sum int,avg int){
	sum=a+b
avg=(a+b)/2
	return
}
上面就是给返回值命名

  

_标识符,用来忽略返回值

func calc(a,b int) (sum int,avg int){
    sum=a+b
    avg=(a+b)/2
    return
}
func main(){
    sum,_=calc(100,200)
}
上面的这个小程序如果只想要和不想要平均值,那么这个时候可以用_来忽略返回值
View Code

可变参数

func add(arg```int) int{   //0个或者多个参数 
}
func add(a int,arg```int)int{  //1个或者多个参数
}
func add(a int,b int,arg```int) int{  //2个或者多个参数
}
注意:其中arg是一个slice,我们可以通过arg[index]依次访问所有参数,通过len(arg)来判断传递参数的个数

 

练习:

1、  写一个函数add,支持一个或者多个int相加,并且返回相加结果

 

package main

import(
   "fmt"
)

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

func main(){
   sum:=add(10)
   fmt.Println(sum)
}

2、	写一个函数concat,支持一个或者多个string相拼接,并返回结果

package main

import (
   "fmt"
)

func concat(a string,arg...string)(result string){
   result=a     //这个是如果arg没有值,就让返回值等于第一个参数
   for i:=0;i<len(arg);i++{
      result+=arg[i]
   }
   return
}

func main(){
   res:=concat("hello"," ","world")
   fmt.Println(res)
}
6、

defer的用途

1、当函数返回的时候执行defer语句,因此,可以用来做资源清理

2、多个defer语句,按照先进后出的方式执行

1、  defer语句中的变量,在defer声明时就决定了

package main

import (
   "fmt"
)

func main(){
   var i int=0
   //下面传入的i也就是传入参数进去,这里的参数就是一个副本
   //执行的时候会把defer压到一个栈里面,等程序执行完毕之后执行这个defer
   defer fmt.Println(i) //defer后面相当于执行一个函数调用,这里取的定义的i的值也就是0

   i=10
   fmt.Println("第二个",i) //先执行这里,然后执行上面的
}
例子:
func f(){
	for i:=0;i<5;i++{
		defer fmt.Printf(“%d”,i)
}
}
这里打印的是4 3 2 1 0

defer用途

1、  关闭文件句柄

func read(){

file:=open(filename)

defer file.close()

}

2、  锁资源释放

func read(){

mc.Lock()

defer mc.Unlock()

//其他操作

}

3、  数据库连接的释放

func read(){

conn:=openDatabase()

defer conn.close()

//其他操作

}

注意:在程序出错的时候就不会执行defer了

defer后面是调用的函数,调用匿名函数

defer func (){

}()  //这里加上括号就是执行这个defer函数

defer执行匿名函数

package main

import (
   "fmt"
)

//匿名函数调用方式一
func test(a,b int)int{
   result:=func(a int,b int) int{
      return a+b
   }(a,b)     //由于这个是匿名函数,所以执行匿名函数就是在后面加上括号,把a和b传递进去
   return result
}

//匿名函数调用方式二
func test2(a,b int) int{
   result:=func(a,b int)int{
      return a+b
   }
   return result(a,b)  //因为匿名函数地址给变量,所以变量来调用,如果不调用匿名函数,那么就调用变量的 方式
}

func main(){
   fmt.Println("方式一",test(100,200))
   fmt.Println("方式二",test2(100,200))
}

还可以在全局定义匿名函数变量
package main 
import(
“fmt”
)
var (
result=func(a,b int) int{
	result=a+b
}
)
func main(){
	fmt.Println(result(100,200))
}

匿名函数后面的括号就是执行匿名函数

原文地址:https://www.cnblogs.com/pyrene/p/8094188.html