all

export GOPATH=/Users/as4k/gopro

获取键盘输入

package main

import "fmt"

func main() {

	var name string
	var age int
	fmt.Println("请输入用户名:")
	// 当使用Scan时,会提示用户输入
	// 用户输入完成之后,会得到两个值:count,用户输入了几个值;err,用输入错误则是错误信息
	_, err := fmt.Scan(&name, &age)
	if err == nil {
		fmt.Println(name, age)
	} else {
		fmt.Println("用户输入数据错误", err)
	}
	// 特别说明:fmt.Scan 要求输入两个值,必须输入两个,否则他会一直等待。

}
// 测试:运行代码,输入两个数据,空格分割,如 ming 20

条件判断

##### 1
if true {
  fmt.Println("666")
} else{
  fmt.Println("999")
}
##### 2
if 1 > 2 {
    fmt.Println("666")
} else {
    fmt.Println("999")
}
##### 3
flag := false
if flag {
    fmt.Println("条件成立")
} else{
    fmt.Println("条件不成立")
}
##### 4
package main
import "fmt"
func main() {
	// 判断用户名密码是否正确
	var username, password string
	fmt.Print("请输入用户名:")
	fmt.Scanln(&username)
	fmt.Print("请输入密码:")
	fmt.Scanln(&password)
	if username == "root" && password == "123456" {
		fmt.Println("欢迎登录")
	} else {
		fmt.Println("用户名或密码错误")
	}
}
##### 5
package main
import "fmt"
func main() {
	// 请输入用户名校验是否是VIP
	var username string
	fmt.Print("请输入用户名:")
	fmt.Scanln(&username)
	if username == "alex" || username == "eric" {
		fmt.Println("欢迎使用")
	} else {
		fmt.Println("请充钱")
	}
}
##### 6 成绩判断
package main
import "fmt"
func main() {
	var score int
	fmt.Print("请输入你的分数:")
	fmt.Scanln(&score)
	if score >= 90 {
		fmt.Println("优秀")
	} else if score >= 80 {
		fmt.Println("良好")
	} else if score >= 60 {
		fmt.Println("及格")
	} else {
		fmt.Println("挂科")
	}
}
##### 7 嵌套判断
package main
import "fmt"
func main() {
	fmt.Println("欢迎致电10086,1.话费相关;2.业务办理;3.人工服务。")
	var number int
	fmt.Scanln(&number)
	if number == 1 {
		fmt.Println("话费服务,1.交话费;2.查询。")
		var n1 int
		fmt.Scanln(&n1)
		if n1 == 1 {
			fmt.Println("缴话费啦")
		} else if n1 == 2 {
			fmt.Println("查话费了")
		} else {
			fmt.Println("输入错误")
		}
	} else if number == 2 {
		fmt.Println("业务办理")
	} else if number == 3 {
		fmt.Println("人工服务")
	} else {
		fmt.Println("输入错误")
	}
	// 建议:条件的嵌套不要太多
}

变量

定义变量方式1
==================================
var name string = "武沛齐"
var age int = 18

定义变量方式2
==================================
var name = "武沛齐"
var age = 18

定义变量方式3
==================================
name := "武沛齐"
age := 18

定义变量方式4
==================================
var name string
name = "武沛齐"

定义变量方式5 定义多个变量 因式分解
==================================
var (
    name   = "武沛齐"
    age    = 18
    gender string
)
var age,age2 = 10,11
var c1, c2, c3 = "chaoge", 18, 99.99

特殊变量,占位符 _
==================================
package main
import "fmt"
func Person(a1 int, n1 string) (int, string) {
	return a1, n1
}
func main() {
	_, name := Person(18, "好嗨哦")
	fmt.Println(name)
}

变量的默认值
==================================
注意:如果变量只声明不赋值,Go内部其实会给变量默认设置值:int 为 0,float 为 0.0,bool 为 false,string 为空字符串,指针为 nil 。

变量名要求
==================================
变量名由字母、数字、下划线组成,且首个字符不能为数字。

全局变量
==================================
package main
import "fmt"
// 声明全局变量
var country string = "中国"
// 或可使用 var country = "中国"
// 不可使用 country := "中国"
func main() {
	fmt.Println(country)
	country = "China"
	fmt.Println(country)
}
// Go中的全局变量比较特殊,如果全局变量名首字母小写,则只能被当前包中的go文件使用,外部无法使用;如果首字母大写,则任意文件都使用全局变量。
// 可简单理解为:首字母大写表写公有,首字母小写表示私有(当前包的go文件所有)。

局部变量
==================================
Go中的变量有作用域之分,每个大括号就是一个作用域,每个作用域中都可定义相关的局部变量。
package main
import (
	"fmt"
)
func main() {
	// 声明局部变量,在当前函数可用
	var name string = "武沛齐"
	fmt.Println(name)
	if true {
		//  生命局部变量,在当前if中可用
		var age = 18
		fmt.Println(age)
	}
	// 报错,age在if括号的作用域中
	// fmt.Println(age)
}

常量

  • 常量代表只读的,不可修改的值,用const关键字定义。
  • 如同用常量定义 “π”之类的常数。
  • 常量如同变量一样,可以批量声明,或者一组相关的常量。
  • 常量的计算都在编译期间完成,并非运行期间!减少运行时的工作。
  • 未使用的常量不会引发编译错误。(这点和变量不一样)

    const World string = "世界" //常量定义且赋值
    const x, y int = 1, 2      //多常量初始化
    const s1 = "Hello golang"  //常量类型推断,字符串类型
    //常量组
    const (
    	e       = 2.71828182845904523536028747135266249775724709369995957496696763
    	pi      = 3.14159265358979323846264338327950288419716939937510582097494459
    	b  bool = true
    )
    
    //常量组,可以除了第一个外其他的常量右边的初始化表达式可以省略
    //如果省略初始化表达式,默认使用前面常量的表达式
    //与上一个常量相同
    const (
    	c1=1
    	c2
    	c3
    	c4="c44444"
    	c5
    )
    /*
    输出结果
    1
    1
    1
    c44444
    c44444
    */
    

iota 常量生成器

iota用于生成一组相似规则初始化的常量,在const常量声明的语句中,第一个常量所在行,iota为0,之后每一个常量声明加1

例如time包的例子,一周7天,每天可以定义为常量,1~6,周日为0,这种类型也称为枚举

package main
import (
	"fmt"
)
const (
	Sunday = iota
	Monday //通常省略后续行表达式
	Tuesday
	Wednesday
	Thursday
	Friday
	Saturday
)
func main() {
	fmt.Println(Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday)
}
//output: 0 1 2 3 4 5 6

整数和浮点数 数据类型查看

package main
import (
	"fmt"
	"unsafe"
)
func main() {
	var n1 = 100
	fmt.Printf("n1的类型:%T \n", n1)

	var n2 int64 = 100
	//unsafe包底下的Sizeof函数,返回变量占用字节数
	fmt.Printf("n2的类型是:%T,占用的字节数是%d \n", n2, unsafe.Sizeof(n2))

	var price float32 = 100.02
	fmt.Printf("price类型是:%T,值是%v \n", price, price)//%T 类型  %v 默认值
}

字符和字符串

  • Go的字符串是由单个字节连接起来的
  • 传统的字符串是由字符组成的,而Go的字符串不同,它是由字节组成的
  • Go的字符用单引号表示
  • Go的字符串用双引号表示

字符

package main
import "fmt"
func main() {
	var c1 byte = 'a'
	var c2 byte = '2' //字符的2
	//直接输出byte的值,也就是输出对应的字符的码值
	fmt.Println("c1=", c1)
	fmt.Println("c2=", c2)
	//输出字符的值,需要格式化输出
	fmt.Printf("c1值=%c  c2值=%c\n", c1, c2)

	//如果保存的字符对应码大于255,应该使用int而不是byte,否则overflows byte异常
	//var c3 int = '皮'  //正确
	//var c4 byte = '皮' //overflows byte 报错
	var c4 int = 22269
	fmt.Printf("c4=%c\n", c4)
}

字符串

package main
import "fmt"
func main() {
	//识别转义符
	var story string = "妖怪\n放了我师父"
	fmt.Println(story)

	//反引号,以原生形式输出,包括特殊字符,防止注入攻击
	story2 := `
    你看这个灯,它又大又亮
你好
我是银角大王吧,你吃了\n吗?
    `
	fmt.Println(story2)
}

字符串拼接

//字符串拼接,识别空格
str3 := "你好" + "  我是孙悟空"
fmt.Println(str3)

字符串单个索引取出

package main
import "fmt"
func main() {
	str3 := "hello world"
	//索引取出值的码值,格式化输出
	fmt.Printf("str3=%c\n",str3[1])
	//输出str3的长度
	fmt.Println(len(str3))
}

字符串遍历

package main
import "fmt"
func main() {
	myname := "hello world"

	//for _, ret := range myname {
	//	fmt.Printf("ret=%c\n", ret)
	//}

	for k, ret := range myname {
		fmt.Println(k,ret)
	}

}

修改字符串

package main
import "fmt"
func main() {
	myname := "hello world"
	m1 := []rune(myname) //转化为[]int32的切片,rune是int32的别名
	m1[4] = '皮'         //修改索引对应的值
	myname = string(m1)  //类型强转,rune转为string
	fmt.Println(myname)
	//output: hell皮 world
}

布尔型

一个布尔类型的值只有两种:true 和 false

package main
import (
    "fmt"
    "unsafe"
)
func main() {
    var b = true
    fmt.Println("b=", b)
    fmt.Println("b字节数=", unsafe.Sizeof(b))
}

字符串常用操作函数

官网模块 https://golang.org/pkg/strings/

package main
import (
    "fmt"
    "strings"
)
func main() {
    str := "hello world"
    //判断是不是以某个字符串开头,返回布尔值
    res0 := strings.HasPrefix(str, "http://")
    res1 := strings.HasPrefix(str, "hello")
    fmt.Printf("res0 is %v\n", res0)
    fmt.Printf("res1 is %v\n", res1)
    //判断是不是以某个字符串结尾
    res3 := strings.HasSuffix(str, "http://")
    res4 := strings.HasSuffix(str, "world")
    fmt.Printf("res3 is %v\n", res3)
    fmt.Printf("res4 is %v\n", res4)
    //判断字符在字符串中首次出现的索引位置,没有返回-1
    res5 := strings.Index(str, "o")
    res6 := strings.Index(str, "x")
    fmt.Printf("res5 is %v\n", res5)
    fmt.Printf("res6 is %v\n", res6)
    //返回字符最后一次出现的索引位置,没有返回-1
    res7 := strings.LastIndex(str, "o")
    res8 := strings.LastIndex(str, "x")
    fmt.Printf("res7 is %v\n", res7)
    fmt.Printf("res8 is %v\n", res8)
    //字符串替换
    res9 := strings.Replace(str, "world", "golang", 2)
    res10 := strings.Replace(str, "world", "golang", 1)
    //trings.Replace("原字符串", "被替换的内容", "替换的内容", 替换次数)
    //原字符串中有2个world,才能替换2次
    fmt.Printf("res9 is %v\n", res9)
    fmt.Printf("res10 is %v\n", res10)
    //求字符在字符串中出现的次数,不存在返回0次
    countTime0 := strings.Count(str, "h")
    countTime1 := strings.Count(str, "x")
    fmt.Printf("countTime0 is %v\n", countTime0)
    fmt.Printf("countTime1 is %v\n", countTime1)
    //重复几次字符串
    res11 := strings.Repeat(str, 0)
    res12 := strings.Repeat(str, 1)
    res13 := strings.Repeat(str, 2)
    // strings.Repeat("原字符串", 重复次数)
    fmt.Printf("res11 is %v\n", res11)
    fmt.Printf("res12 is %v\n", res12)
    fmt.Printf("res13 is %v\n", res13)
    //字符串改大写
    res14 := strings.ToUpper(str)
    fmt.Printf("res14 is %v\n", res14)
    //字符串改小写
    res15 := strings.ToLower(str)
    fmt.Printf("res15 is %v\n", res15)
    //去除首尾的空格
    res16 := strings.TrimSpace(str)
    fmt.Printf("res16 is %v\n", res16)
    //去除首尾指定的字符,遍历l、d、e然后去除
    res17 := strings.Trim(str, "ld")
    fmt.Printf("res17 is %v\n", res17)
    //去除开头指定的字符
    res18 := strings.TrimLeft(str, "he")
    fmt.Printf("res18 is %v\n", res18)
    //去除结尾指定的字符,遍历d、l、r
    res19 := strings.TrimRight(str, "dlr")
    fmt.Printf("res19 is %v\n", res19)
    //用指定的字符串将string类型的切片元素结合
    str1 := []string{"hello", "world", "hello", "golang"}
    res20 := strings.Join(str1, "+")
    fmt.Printf("res20 is %v\n", res20)
}

指针

//1. 指针默认值nil
//2. 通过&(取地值符)取变量地址
//3. 通过*(取值符)透过指针访问目标值

package main
import (
	"fmt"
)
func main() {
	var name string = "yugo"
	//查看name变量的内存地址,通过&name取地址
	fmt.Printf("name的内存地址:%v\n", &name)

	//指针变量,存的是内存地址
	//ptr指针变量指向变量name的内存地址
	var ptr *string = &name
	fmt.Printf("指针变量ptr的内存地址:%v\n", ptr)

	//获取指针变量的值,用*ptr
	//*ptr表示读取指针指向变量的值
	fmt.Printf("指针变量ptr指向的值是:%v\n", *ptr)
}

数组

https://pythonav.com/wiki/detail/4/47/

  • 数组是多个相同类型数据的组合,且长度固定,无法扩容

    ####1
    package main
    import "fmt"
    func main() {
    	var a1 = [...]int{1, 2, 3, 4, 5, 6}
    	//通过索引取值
    	for i := 0; i < len(a1); i++ {
    		fmt.Println(a1[i])
    	}
    
    	//for循环遍历数组,索引和值,index可以省略用占位符_
    	for index, value := range a1 {
    		fmt.Println(index, value)
    	}
    }
    
    ####2
    package main
    import (
    	"fmt"
    )
    //函数接收值类型,默认有值拷贝
    func test(arr [3]int) {
    	arr[0] = 66
    }
    //函数修改原本数组,需要使用引用传递
    func test2(arr *[3]int) {
    	(*arr)[0] = 66 //可以缩写arr[0]=66 编译器自动识别,arr是指针类型
    }
    func main() {
    	//声明arr数组,需要考虑传递函数参数时,数组的长度一致性
    	arr := [3]int{11, 22, 33}
    	//test函数不会修改数组
    	test(arr)
    	fmt.Println(arr)
    	//test2修改了数组
    	test2(&arr)
    	fmt.Println(arr)
    }
    

切片 Slice

https://pythonav.com/wiki/detail/4/48/

//1. 切片是可动态变化的序列,是对数组的引用,引用类型,遵循引用传递的机制
//2. slice类型写作[]T,T是slice元素类型,var s1 []int,s1就是切片变量

/*
区间索引时,Go言里也采用左闭右开形式
比如 
a = [1, 2, 3, 4, 5]
a[0:3] = [1, 2, 3] 不包含最后一个元素
比如
s[m:n]这个切片,
0 ≤ m ≤ n ≤ len(s),包含 n-m 个元素
*/


package main
import "fmt"
func main() {
	//创建一个数组
	var array1 [5]int = [...]int{11, 22, 33, 44, 55}
	/*
		创建切片,通过对数组的索引切片
		s1 是切片名
		array1[1:3]代表slice引用数组区间,索引1到索引3的值,注意取头不取尾,
	*/
	s1 := array1[1:4]
	fmt.Println(array1)
	fmt.Println(s1)
	fmt.Println(len(s1))
	fmt.Println(cap(s1))
}
/*
运行结果
    [11 22 33 44 55]      //原本数组
    [22 33 44]            //切片的值
    3                     //切片元素长度
    4                     //切片容量
*/

Map哈希表

//1. map是key-value类型数据结构,读作(哈希表、字典),是一堆未排序的键值对集合。
//2. map的key必须是支持相等运算符==、!=的类型,如int、bool、channel、string、pointer、array、sruct、interface。
package main
import "fmt"
func main() {
	/*
	   map声明语法
	   var 变量名  map[keytype]valuetype
	   var m1 map[string]string
	   var m2 map[int]string
	   var m3 map[int]map[string]string//map的value又是map
	   注意map声明不会分配内存,必须make初始化才可以使用
	*/
	//声明map方式一
	//make函数可以合理申请一大块内存,避免后续扩张时性能问题
	var m1 map[string]string
	//标注map的初始容量为10
	m1 = make(map[string]string, 10)
	m1["一号"] = "大狗子"
	m1["二号"] = "二狗子"
	fmt.Println(m1)
	//声明方式二
	m2 := make(map[string]string)
	m2["男"] = "小黑"
	m2["女"] = "小女"
	fmt.Println(m2)
	//声明方式三
	m3 := map[string]string{
		"坦克": "德玛西亚",
		"射手": "伊泽瑞尔",
		"辅助": "星女",
	}
	m3["打野"] = "赵信"
	fmt.Println(m3)
}

Struct 结构体

https://pythonav.com/wiki/detail/4/50/

//1. Golang支持OOP面向对象编程
//2. Go的结构体struct如同python的class
//3. 将一类事务特性提取出一个新的数据类型,就是结构体
//4. 通过结构体可以创建多个实例

package main
import "fmt"
type Person struct {
	Name string
	Age  int
}
func main() {
	//声明方式
	p1 := Person{"小黑", 18} //有序赋值,并且必须包含所有字段,否则报错
	p2 := Person{Age: 18}  //关键词赋值,未赋值字段有空值
	fmt.Println(p1)
	fmt.Println(p2)
}

Go if-else

https://pythonav.com/wiki/detail/4/51/

package main
import "fmt"
func main() {
	var age int
	fmt.Println("请输入您的年纪:")
	//获取用户输入,传入变量地址,防止值拷贝
	fmt.Scanln(&age)
	if age > 18 {
		fmt.Println("你已经是个18岁的小伙子了!!加油")
	}
}

Go switch

package main
import "fmt"
func main() {
	var week int
	fmt.Println("请输入星期几:")
	fmt.Scanln(&week)
	switch week {
	case 1:
		fmt.Println("星期一,上班!!")
	case 2, 3, 4, 5:
		fmt.Println("星期二到星期五,你还得上班!!")
	case 6:
		fmt.Println("周六你就想休息?加班!!")
	case 7:
		fmt.Println("老子迟早要辞职,终于能休息了!!")
	default:
		fmt.Println("输入错误你就必须得上班!!")
	}
}

Go for

package main
import "fmt"
func main() {
	//创建方式一,循环条件是布尔值表达式
	num := 0
	for num <= 10 {
		fmt.Println("我说老男孩golang 你说哟", num)
		num++
	}
	//创建方式二,无限循环,go不存在while语法
	num1 := 0
	for {
		if num1 <= 10 {
			fmt.Println("人生苦短 说go就go", num1)
		} else {
			break //超出了就终止这个for循环
		}
		num1++ //等于num1=num1+1
	}
	fmt.Println("----------")
	//创建方式三  for-range  用于遍历字符串、数组
	var teacher = "wu pei qi"
	//字符串可以用索引取值,注意格式化输出的时候,要输出码值对应的字符  %c 格式化
	for i := 0; i < len(teacher); i++ {
		fmt.Printf("%c\n", teacher[i])
	}
	fmt.Println("----------")
	//创建方式四
	student := "chaoge牛逼"
	//for range遍历,是按照字符方式遍历,支持中文
	for k, v := range student {
		fmt.Printf("索引:%v 值:%c\n", k, v)
	}
	fmt.Println("----------")
	//传统遍历字符串是按字节遍历,汉字对应utf8编码是3个字节
	var class1 string = "python全栈开发班"
	//必须转化为[]rune切片类型,方可使用
	class2 := []rune(class1)
	for i := 0; i < len(class2); i++ {
		fmt.Printf("%c\n", class2[i])
	}
}

循环限制三次登录

package main
import "fmt"
func main() {
	var name string
	var pwd string
	var logincache = 3
	//循环限制三次登录
	for i := 1; i <= 3; i++ {
		fmt.Println("请输入账号:")
		fmt.Scanln(&name)
		fmt.Println("请输入密码:")
		fmt.Scanln(&pwd)
		if name == "alex" && pwd == "alex3714" {
			fmt.Println("欢迎鸡汤王归来!!")
			break
		} else {
			logincache-- //每次登录失败减一
			fmt.Printf("你还有%v次机会尝试,老铁\n", logincache)
		}
	}
}

goto continue break return

goto

package main
import "fmt"
func main() {
	var num int = 100
	fmt.Println("num值100")
	if num > 90 {
		goto label
		//此处代码已经不走,直接goto了
		fmt.Println("呵呵")
	}
	fmt.Println("我是占位符")
	fmt.Println("我是占位符")
	fmt.Println("我是占位符")
	fmt.Println("我是占位符")
	fmt.Println("我是占位符")
	//触发了goto,进入本次标签
label:
	fmt.Println("由于触发了goto,进入到我这里了")
	fmt.Println("我也是占位符")
	fmt.Println("我也是占位符")
	fmt.Println("我也是占位符")
}

break

package main
import "fmt"
func main() {
	var num int = 10
	for num < 50 {
		fmt.Printf("a的值是:%v\n", num)
		num++
		if num > 30 {
			break //跳出for循环
		}
	}
}

break label

package main
import (
	"fmt"
)
func main() {
	fmt.Println("主程序开始执行")
Exit:
	for i := 0; i < 9; i++ {
		for j := 0; j < 9; j++ {
			if i+j > 15 {
				fmt.Println("程序结束")
				break Exit
			}
		}
	}
	fmt.Println("已跳出循环体")
}

continue语句

package main
import "fmt"
func main() {
	/* 定义局部变量 */
	var a int = 10
	/* for 循环 */
	for a < 20 {
		if a == 15 {
			/* 当a等于15时,跳出循环,让a++,等于16,跳过本次循环 */
			a++
			continue
		}
		fmt.Printf("a 的值为 : %d\n", a)
		a++
	}
}

return语句

package main
import "fmt"
func main() {
	for i := 0; i <= 10; i++ {
		if i == 5 {
			return //直接退出main函数了
		}
		fmt.Printf("本次循环次数:%d\n", i)
	}
	//永远走不带这里了,第五次for循环时候,直接return了
	fmt.Println("循环结束,走到了我")
}

函数

https://pythonav.com/wiki/detail/4/52/

关键词

  • init 函数
  • 闭包
  • defer

    func 函数名(形参列表)(返回值列表){
    执行代码
    return 返回值列表
    }
    

init函数 每个源文件都会包含一个inti函数,该函数在main函数之前被执行

package main
import "fmt"
func init() {
	fmt.Println("init函数一般完成初始化工作,如数据库驱动连接等")
}
func main() {
	fmt.Println("我是主程序")
}
// 全局变量 --> init函数 --> main函数

面试题:如果再包导入中,main.go和utils.go都有变量加载,init函数,执行流程是?

main.go是程序入口
↓
自上而下,进入import导入
↓
优先进入utils.go 加载全局变量  这是第一步
↓
执行utils.go的init函数        第二步
↓
完毕后,回到main.go的全局变量    第三步
↓
执行main.go的init函数        第四步
↓
执行main.go的主程main()函数    第五步

strings处理字符串相关

统计字符串长度,按字节                   len(str)
字符串遍历,处理中文                    r:=[]rune(str)
字符串转整数                           n, err := strconv.Atoi("12")
整数转字符串                           str = strconv.Itoa(12345)
字符串 转 []byte                   var bytes = []byte("hello go")
[]byte 转 字符串                   str = string([]byte{97, 98, 99})
10 进制转 2, 8, 16 进制:              str = strconv.FormatInt(123, 2) // 2-> 8 , 16
查找子串是否在指定的字符串中             strings.Contains("seafood", "foo") //true
统计一个字符串有几个指定的子串         strings.Count("ceheese", "e") //4
不区分大小写的字符串比较(==是区分字母大小写的)  fmt.Println(strings.EqualFold("abc", "Abc")) // true
返回子串在字符串第一次出现的 index 值,如果没有返回-1      strings.Index("NLT_abc", "abc") // 4
返回子串在字符串最后一次出现的 index,如没有返回-1         strings.LastIndex("go golang", "go")
将指定的子串替换成 另外一个子串 strings.Replace("go go hello", "go", "go 语言", n) ,n 可以指 定你希望替换几个,如果 n=-1 表示全部替换
按照指定的某个字符,为分割标识,将一个字符串拆分成字符串数组  strings.Split("hello,wrold,ok", ",")
将字符串的字母进行大小写的转换: strings.ToLower("Go") // go strings.ToUpper("Go") // GO
将字符串左右两边的空格去掉: strings.TrimSpace(" tn a lone gopher ntrn ")
将字符串左右两边指定的字符去掉 : strings.Trim("! hello! ", " !") 
将字符串左边指定的字符去掉 : strings.TrimLeft("! hello! ", " !")
将字符串右边指定的字符去掉 :strings.TrimRight("! hello! ", " !")
判断字符串是否以指定的字符串开头: strings.HasPrefix("ftp://192.168.10.1", "ftp") 
判断字符串是否以指定的字符串结束: strings.HasSuffix("NLT_abc.jpg", "abc") //false

捕获异常

https://pythonav.com/wiki/detail/4/52/

package main
import "fmt"
func test() {
	//在函数退出前,执行defer
	//捕捉异常后,程序不会异常退出
	defer func() {
		err := recover() //内置函数,可以捕捉到函数异常
		if err != nil {
			//这里是打印错误,还可以进行报警处理,例如微信,邮箱通知
			fmt.Println("err错误信息:", err)
		}
	}()
	//如果没有异常捕获,直接报错panic,运行时出错
	num1 := 10
	num2 := 0
	res := num1 / num2
	fmt.Println("res结果:", res)
}
func main() {
	test()
	fmt.Println("如果程序没退出,就走我这里")
}

单元测试

➜  test pwd
/Users/as4k/xdev/test
➜  test ls
calc.go      calc_test.go
➜  test cat calc.go 
package main
func add(a, b int) int {
    return a + b
}
func sub(a, b int) int {
    return a - b
}
➜  test cat calc_test.go 
package main
import (
	"testing"
)
func TestAdd(t *testing.T) {
	r := add(2, 4)
	if r != 6 {
		t.Fatalf("add(2, 4) error, expect:%d, actual:%d", 6, r)
	}
	t.Logf("test add succ")
}
➜  test go test -v      
=== RUN   TestAdd
    calc_test.go:10: test add succ
--- PASS: TestAdd (0.00s)
PASS
ok  	_/Users/as4k/xdev/test	0.675s

方法

https://pythonav.com/wiki/detail/4/53/

// I
package main
import "fmt"
//定义一个结构体数据类型
type Person struct {
	Username string
	Age      int
	Sex      string
}
//表示给Person结构体,绑定添加test方法
func (p Person) test() {
	fmt.Println("通过p变量,取出结构体类型中的Username值是:", p.Username)
}
func main() {
	p1 := &Person{
		"狗子",
		18,
		"男",
	}
	p1.test()
}

test方法和Person结构体类型绑定
test方法只能通过Person结构体的实例调用

// II
func (recevier type) methodName(参数列表)(返回值列表){}

package main
import "fmt"
//定义一个结构体数据类型
type Person struct {
	Username string
	Age      int
	Sex      string
}
//此时这个(p Person)就是一个接受者
//Person结构体,人是可以说话的,添加speak方法
func (p Person) speak() {
	fmt.Printf("大声的喊出了自己的名字:%v\n", p.Username)
}
//人还可以蹦跳
func (p Person) jump() {
	fmt.Printf("%v:跳起来一拳打在了姚明的膝盖上\n", p.Username)
}
//人还可以进行算数
//方法的参数列表与返回值列表,与函数一致
func (p Person) getSum(n1, n2 int) int {
	sum := n1 + n2
	fmt.Printf("%v:飞快的计算出%d+%d的结果是%d\n", p.Username, n1, n2, sum)
	return sum
}
func main() {
	p1 := &Person{
		"李二狗",
		18,
		"男",
	}
	p1.speak()
	p1.jump()
	res := p1.getSum(1, 2)
	fmt.Printf("p1.getSum方法返回值是%d\n", res)
}

// III
1)结构体类型是值类型,在方法调用中,遵守值类型的传递机制,是值拷贝传递方式
2)如程序员希望在方法中,修改结构体变量的值,可以通过结构体指针的方式来处理
3)当接受者不是一个指针时,该方法操作对应接受者的值的副本(意思就是即使你使用了指针调用函数,但是函数的接受者是值类型,所以函数内部操作还是对副本的操作,而不是指针操作
package main
import "fmt"
type People struct {
	Name    string
	Country string
}
//此方法进行了值拷贝
func (p People) Print() {
	fmt.Printf("我是谁:name=%s country=%s\n", p.Name, p.Country)
}
//此方法进行了值拷贝,不会对p1进行修改
func (p People) Set(name string, country string) {
	p.Name = name
	p.Country = country
}
//接收一个指针变量,可以修改原值
func (p *People) SetV2(name string, country string) {
	p.Country = country
	p.Name = name
}
func main() {
	var p1 People = People{
        Name:    "二狗子",
        Country: "沙河",
	}
	p1.Print()
	//此处修改无效,并没有修改p1的原地址
	p1.Set("二狗腿子", "日本")
	p1.Print()
	//两者效果一样,是被编译器进行了优化
	//(&p1).SetV2("people02", "english")
	p1.SetV2("狗官", "日本")
	p1.Print()
}

接口 interface

https://pythonav.com/wiki/detail/4/54/

并发

https://pythonav.com/wiki/detail/4/55/

go 项目

https://docs.cloudreve.org/

交叉编译

https://www.cnblogs.com/wangyang0210/p/12986887.html

#linux
export GOOS=linux 
export GOARCH=amd64

#macos
export GOOS=darwin
export GOARCH=amd64