Golang语言学习

2023年将会持续于B站、CSDN等各大平台更新,可加入粉丝群与博主交流:838681355,为了老板大G共同努力。
【商务合作请私信或进群联系群主】

一、说明

Golang(Google):静态强类型、编译型、并发型、垃圾回收
具有python开发效率、又有C的高性能
严格区分大小写

环境变量设置:
系统变量新建GOROOT,变量值C:\Program Files\Go\ -> 修改PATH C:\Program Files\Go\bin
更换源:
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct

go项目结构:
    src:源代码文件
    pkg:包文件
    bin:相关bin文件

运行go项目:go run main.go
打包go项目:go build main.go
代码内容格式化:go fmt main.go

二、包和函数(package & function)

# 程序由main开始执行
package main (声明代码所属的main包)
import ("fmt")  (所需要使用的包叫fmt)

func main() (声明一个main()函数)
{fmt.Println("Hello,playgounrd")}

三、GO特征

1.自动立即回收。
2.更丰富的内置类型。
3.函数多返回值。
4.错误处理。
5.匿名函数和闭包。
6.类型和接口。
7.并发编程。
8.反射。
9.语言交互性。

四、GO基础

3.1 标识符、关键字、命名规则

Go的函数、变量、常量、自定义类型、包(package)的命名方式遵循以下规则:
标识符组成:
1.由数字、字母下划线(_)组成
2.只能数字下划线(_)开头
3.标识符区分大小写

关键字:
    break        default      func         interface    select
    case         defer        go           map          struct
    chan         else         goto         package      switch
    const        fallthrough  if           range        type
    continue     for          import       return       var

保留字:
    Constants:    true  false  iota  nil
    Types:    int  int8  int16  int32  int64  
              uint  uint8  uint16  uint32  uint64  uintptr
              float32  float64  complex128  complex64
              bool  byte  rune  string  error
    Functions:   make  len  cap  new  append  copy  close  delete
                 complex  real  imag
                 panic  recover

3.2 声明定义

var(声明变量) var 变量名 变量类型
const(声明常量)
type(声明类型)
func(声明函数)
:= 短变量声明(只能放在函数内部)

3.3 内置类型和函数

引用类型(指针):
    slice   -- 序列数组(最常用)
    map     -- 映射
    chan    -- 管道
内置函数:
    append          -- 用来追加元素到数组、slice中,返回修改后的数组、slice
    close           -- 主要用来关闭channel
    delete            -- 从map中删除key对应的value
    panic            -- 停止常规的goroutine  (panic和recover:用来做错误处理)
    recover         -- 允许程序定义goroutine的panic动作
    real            -- 返回complex的实部   (complex、real imag:用于创建和操作复数)
    imag            -- 返回complex的虚部
    make            -- 用来分配内存,返回Type本身(只能应用于slice, map, channel)
    new                -- 用来分配内存,主要用来分配值类型,比如int、struct。返回指向Type的指针
    cap                -- capacity是容量的意思,用于返回某个类型的最大容量(只能用于切片和 map)
    copy            -- 用于复制和连接slice,返回复制的数目
    len                -- 来求长度,比如string、array、slice、map、channel ,返回长度
    print、println     -- 底层打印函数,在部署环境中建议使用 fmt 包
内置结构error:
    type error interface { //只要实现了Error()函数,返回值为String的都实现了err接口
            Error()    String
    }

3.4 运算符

算术运算符:+ - * / %
关系运算符:== != > >= < <=
逻辑运算符:&& || !
位运算符:& 1 ^ >> <<
赋值运算符:= += -= *= /= %= <<= >>= &= 1= ^=

3.5 下划线

func main() {
    buf := make([]byte, 1024)
    f, _ := os.Open("/Users/***/Desktop/text.txt")
// 比如os.Open,返回值为*os.File,error, 普通写法是f,err := os.Open("xxxxxxx"),如果此时不需要知道返回的错误值就可以用f, _ := os.Open("xxxxxx")如此则忽略了error变量

    defer f.Close()
    for {
        n, _ := f.Read(buf)
        if n == 0 {break}os.Stdout.Write(buf[:n])}}

3.6 数组与切片(Array/Slice)

数组定义:var a [len]int
切片的定义:var 变量名 []类型,比如 var str []string  var arr []int
[...]省略数组长度,做初始化处理

3.6.1 循环数组

func main(){
	var a = [5]int{1,2,3,4,5}
	for i, v := range a{
		fmt.Printf("i:%d v:%v\n", i , v)
	}
}

3.6.2 切片

示例一:
func main(){
	var a = []int{1,2,3,4,5}
	for _, v :=range a{
		fmt.Printf("%v\n", v)
	}
}

示例二:
func main(){
    var s2 []int
    var s2 = make([]int,2)
    fmt.Printf("s2:%v\n",s2)
}

3.6.3 数组切片增删改查

// 添加
func addtest(){
	var s1 = []int{}
	s1 = append(s1,100)
	s1 = append(s1,200)
	s1 = append(s1,300)
	fmt.Print(s1)
}

// 删除
func delecttest(){
	var s1 = []int{1,2,3,4,5}
	s1 = append(s1[:0],s1[:2]...)
	// a = append(a[:index],a[index+1]...)
	fmt.Print(s1)
}

// 修改
func update(){
	var s1 = []int{1,2,3,4}
	s1[1] = 100
	fmt.Print(s1)
}

// 查询
func query(){
	var s1 = []int{1,2,3,4}
	var key = 2
	for i , v := range s1{
		if v == key{
			fmt.Printf("i:%v\n",i)
		}
	}
}

// 复制
func copy(){
	var s1 = []int{1,2,3,4}
	var s2 = s1
	s2[0] = 100
	fmt.Printf("s2,%v",s2)
}

3.7 指针

两个符号:&(取地址)、*(根据地址取值)
类型指针不能偏移和运算。

指针地址:
func main() {
    //指针取值
    a := 10
    b := &a // 取变量a的地址,将指针保存到b中
    fmt.Printf("type of b:%T\n", b)
    c := *b // 指针取值(根据指针去内存取值)
    fmt.Printf("type of c:%T\n", c)
    fmt.Printf("value of c:%v\n", c)
}

实例:
func main(){
	var a int = 20 /* 声明实际变量 */
	var ip *int /* 声明指针变量 */
	ip = &a /* 指针变量存储地址 */
	fmt.Printf("a的变量地址为:%x\n",&a)
	fmt.Printf("ip变量储存的指针地址:%x\n",ip)
	fmt.Printf("*ip变量的值:%d\n",*ip)
}

3.7.1 指向数组的指针

语法:
var ptr [MAX]*int; //表示数组里面的元素的类型是指针类型

实例:
func main(){
	a := [3]int{1,2,3}
	var pa [3]*int
	fmt.Printf("pa:%v\n",pa)
	for i :=0 ; i

3.8 输入与输出

func main(){
	fmt.Println("输入:")
	var a string
	fmt.Scanf("%s",&a)
	fmt.Println(a)
}

3.8.1 字符串连接(+)

func main(){
	s1 := "tom"
	s2 := "hello"
	s3 := s1+s2
	fmt.Printf("%s", s3)
}

3.8.2 字符串连接(buffer.WriteString)

func main(){
	var buffer bytes.Buffer
	buffer.WriteString("tom")
	buffer.WriteString(",")
	buffer.WriteString("20")
	fmt.Printf("buffer.String():%v\n",buffer.String())
}

3.9 循环

3.9.1 for循环

示例一:
func f(){
    for i:=0 ; i<10 ; i++{ // i等于0,i小于10时,每次循环i+1
		fmt.Println(i)
	}
}
                    
示例二:
func f(){
    i:=0
    for ; i<10 ; i++{ // i等于0,i小于10时,每次循环i+1
		fmt.Println(i)
	}
}

3.9.2 _和for range循环

func main(){
	a := [...]int{1,2,3,4}
	fmt.Println(a)
	var b = [...]string{"北京","上海","中山"}
	fmt.Println(b)
	for _,c := range b{
		fmt.Println(c)
	}
}

3.9.3 永真循环

func f(){
    for {
        fmt.Println("循环")
    }
}

3.9.4 Goto(跳出循环)

break:跳出第一层循环
continue: 继续循环
Goto:跳出多层循环

func main(){
	i := 1
	if i >= 2 {
		fmt.Println("2")
	} else {
		goto END
	}
END:
	fmt.Println("END....")
}

3.12 结构体

type person struct{
	name string
	age  int
	city  string
}
func main(){
	var p1 person
	p1.name="123"
	p1.city="ch"
	p1.age=1
	fmt.Println(p1.name,p1.city,p1.age)
}

3.13 接口

type hello interface{ // 声明接口hello
	say()
}
type dog struct{} //结构体dog
type cat struct{} //结构体cat
func (d dog) say(){   // 用dog调用say(),实现接口hello
	fmt.Print("111")
}
func (c cat) say(){ // 用cat调用say(),实现接口hello
	fmt.Print("222")
}
func main(){
	var x hello // 声明一个hello类型得变量x
	b := dog{} // 实例化dog赋予b
	c := cat{} // 实例化cat赋予c
	x = b // 将b赋予变量x
	x.say()
	x = c 
	x.say()
}

3.14 错误处理

示例一:
func main(){
	ret,err := getCircleAreaII(-5)
	if err!=nil{
		fmt.Println(err)
	}else{
		fmt.Println("ret=",ret)
	}
}
func getCircleAreaII(radius float32)(ret float32,err error) {
    if radius < 0{
        err = errors.New("傻鸟,半径不能为负")
        return
    }
    ret = 3.14 * radius * radius
    return
}
                  
示例二:
//正确写法
if err != nil{
	//错误处理
	return //或者继续
}
//正常代码

3.15 go func

func main() {
    runtime.GOMAXPROCS(1) //设置协程调度只有一个P
    wg := sync.WaitGroup{}
    wg.Add(20)
    for i := 0; i < 10; i++ {
        go func() {
            fmt.Println("A: ", i)
            wg.Done()
        }()
    }
    for i := 0; i < 10; i++ {
        go func(i int) {
            fmt.Println("B: ", i)
            wg.Done()
        }(i)
    }
    wg.Wait()
}
//https://studygolang.com/articles/21588?fr=sidebar

3.16 并发

var wg sync.WaitGroup // 申明一个信号量的全局变量

func hello(i int) {
	defer wg.Done() // goroutine结束就登记-1
	fmt.Println(i)
}

func main() {
	var begin =time.Now()
	for i := 1; i < 9000; i++ {
		wg.Add(1) // 启动一个goroutine就登记+1
		go hello(i)
	}
	wg.Wait() // 等待所有登记的goroutine都结束再执行下一步
	var elapseTime = time.Now().Sub(begin)
	fmt.Println("耗时:", elapseTime)
}

WaitGroup对象不是一个引用类型,在通过函数传值的时候需要使用地址:*sync.WaitGroup

func main() {
    wg := sync.WaitGroup{}
    wg.Add(100)
    for i := 0; i < 100; i++ {
        go f(i, &wg)
    }
    wg.Wait()
}

// 一定要通过指针传值,不然进程会进入死锁状态
func f(i int, wg *sync.WaitGroup) { //这样子就不用定义全局变量
    fmt.Println(i)
    wg.Done()
}

3.17 channel通道

单向通道的定义:
chan<- int是一个只能发送的通道,可以发送但是不能接收; 存入
<-chan int是一个只能接收的通道,可以接收但是不能发送。 取出.
定义的通道是否缓冲,需要看缓冲大小的值有没有定义。
make(chan 元素类型, [缓冲大小])

3.18 正则匹配

    func main() {
        // 原生字符串
        buf := `
    DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <title>C语言中文网 | Go语言入门教程title>
    head>
    <body>
        <div>Go语言简介div>
        <div>Go语言基本语法
        Go语言变量的声明
        Go语言教程简明版
        div>
        <div>Go语言容器div>
        <div>Go语言函数div>
    body>
    html>
        `
        //解释正则表达式
        reg := regexp.MustCompile(`<div>(?s:(.*?))div>`)
        if reg == nil {
            fmt.Println("MustCompile err")
            return
        }
        //提取关键信息
        result := reg.FindAllStringSubmatch(buf, -1)
        //过滤<>
        for _, text := range result {
            fmt.Println("text[1] = ", text[1])
        }
    }

3.19 go socket

https://blog.csdn.net/tianlongtc/article/details/80163661#:~:text=Go%E6%98%AF%E8%87%AA%E5%B8%A6runtime%E7%9A%84%E8%B7%A8%E5%B9%B3%E5%8F%B0%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%EF%BC%8CGo%E4%B8%AD%E6%9A%B4%E9%9C%B2%E7%BB%99%E8%AF%AD%E8%A8%80%E4%BD%BF%E7%94%A8%E8%80%85%E7%9A%84tcp%20socket%20api%E6%98%AF%E5%BB%BA%E7%AB%8BOS%E5%8E%9F%E7%94%9Ftcp%20socket%E6%8E%A5%E5%8F%A3%E4%B9%8B%E4%B8%8A%E7%9A%84%E3%80%82,%E7%94%B1%E4%BA%8EGo%20runtime%E8%B0%83%E5%BA%A6%E7%9A%84%E9%9C%80%E8%A6%81%EF%BC%8Cgolang%20tcp%20socket%E6%8E%A5%E5%8F%A3%E5%9C%A8%E8%A1%8C%E4%B8%BA%E7%89%B9%E7%82%B9%E4%B8%8E%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86%E6%96%B9%E9%9D%A2%E4%B8%8EOS%E5%8E%9F%E7%94%9F%E6%8E%A5%E5%8F%A3%E6%9C%89%E7%9D%80%E4%B8%80%E4%BA%9B%E5%B7%AE%E5%88%AB%E3%80%82

3.20 判断

3.20.1 if-else判断

注意点:
err = msg.WriteMsg(conn,loginMsg);
	if err != nil{
		return
	}
同等于:if err = msg.WriteMsg(conn, loginMsg); err != nil {return}

示例:
func main(){
	x := 0
	if n := "abc"; x>0{
		fmt.Println(n[2])
	}else if x<0{
		fmt.Println(n[1])
	}else{
		fmt.Println(n[0])
	}
}

3.20.2 if嵌套判断

func main(){
	var a int = 100
	var b int = 200
	if a == 100{
		if b == 200{
			fmt.Printf("yes")
		}
	}
	fmt.Printf("%d",a)
}

3.20.3 switch语句

func main(){
	var grade string = "8"
	var marks int = 90
	switch marks{
		case 90: grade = "A"
		case 80: grade = "B"
		case 50,60,70 : grade = "C"
		default: grade = "D"  
	}
	switch {
		case grade == "A" :
		fmt.Printf("优秀!\n" )     
		case grade == "B", grade == "C" :
		fmt.Printf("良好\n" )      
		case grade == "D" :
		fmt.Printf("及格\n" )      
		case grade == "F":
		fmt.Printf("不及格\n" )
		default:
		fmt.Printf("差\n" )
	}
	fmt.Printf("你的等级是 %s\n", grade )
}

3.20.4 逻辑表达式

func main(){
	age := 18
	gender := "男"
	if age >= 18 && gender == "男"{
		fmt.Println("成年")
	}
}

3.21 延迟调用

函数:defer()
特性:先进后出
func main() {
    var whatever [5]struct{}
    for i := range whatever {
        defer fmt.Println(i)
    }
}

四、一些用法

4.1 golang map

map是一种key:value键值对的数据结构容器。map内部实现哈希表。
map最重要一点通过Key来快速检索数据,key类似于索引,指向数据的值。
map是引用类型。

格式:
/*声明变量,默认map是nil*/
var map_variable map[key_data_type]value_data_type
/*使用Make函数*/
map_variable = make(map[key_data_type]value_data_type)

map_variable: map变量名称
key_data_type: key的数据类型
value_data_type: 值得数据类型

4.1.1 map实例

声明使用:
func maptest(){
	var m  map[string]string
	m = make(map[string]string)
	m["name"] = "tom"
	m["age"] = "18"
	m["email"] = "[email protected]"
	for key, value := range m {
		fmt.Print(key+":"+value+" ")
	}
}

遍历:
func maptest(){
	var m  map[string]string
	m = make(map[string]string)
	m["name"] = "tom"
	m["age"] = "18"
	m["email"] = "[email protected]"
	for key, _ := range m {
		fmt.Print(key+":"+" ")
	}
}

4.2 golang函数

1.3种函数:普通函数、匿名函数、方法函数
2.不允许函数重载(同名)
3.不能嵌套函数但可嵌套匿名函数
4.函数参数可以无名称

语法调用:
func function_name([parameter list]){return_types}
{
    函数体
}

解析:
func:函数声明
function_name:函数名称
[parameter list]:参数列表,当函数被调用时,将值传递给参数。
return_types:返回类型,函数返回一列值。

4.2.1 函数定义

示例:
func sum(a int, b int)(ret int){
	ret = a+b
	return ret
}
func main(){
	r := sum(1,2)
	fmt.Print(r)
}

4.2.2 函数调用

func main(){
	r := sum(1,2)
	fmt.Print(r)
}

4.2.3 返回值

有返回值实例:
func sum(a int, b int)(ret int){
	ret = a+b
	return ret
}
func main(){
	r := sum(1,2)
	fmt.Print(r)
}

无返回值实例:
func sum(){
    fmt.Print("hello")
}
func main(){
    sum()
}

多个返回值实例:
func sum()(name string,age int){
    name="hello"
    age=18
    return //也等于return name,age
}
func main(){
	n,a := sum()
	fmt.Print(n,a)
}

4.2.4 函数传参

func sum(a int,b int)int{ //形参
	return a+b
}
func f1(a int){ // Copy
    a = 100
}
func f2(s []int){ // 切片替换
	s[0] = 1000
}
func main(){
	s := []int{1,2,3}
	f2(s)
	fmt.Print(s)
}

4.2.5 变长参数

func test(args ...int){ // 多参数遍历
	for _,v := range args{
		fmt.Print(v)
	}
}
func main(){
	test(1,2,3,4)
	test(22,11,33)
}

4.2.6 函数类型和变量

type定义函数类型:
type fun func(int,int)int // 定义一个fun函数类型,接受两个int类型参数,返回int返回值

实例:
func sum(a int,b int)int{
	return a+b
}
func max(a int,b int)int{
	if a>b{
		return a
	}else{
		return b
	}
}
func main(){
	type test123 func(int,int)int
	var test test123
	test = max
	r := test(1,2)
	fmt.Print(r)
}

4.2.7 匿名函数

go语言函数不能嵌套,但是在函数内部可以定义匿名函数,实现一下简单功能调用。
语法格式:
func (参数列表)(返回值)
可以既没有参数也没有返回值

实例:
func main(){
    max := func(a int, b int) int{
    if a>b{
        return a
    } else{
        return b 
    }
}
    i := max(1,2)
    fmt.Print(i)
}

4.2.8 闭包

定义在一个函数内部的函数。本质上,闭包是将函数内部和函数外部连接起来的桥梁,胡总和说是函数和其引用环境的组合体。
闭包 = 函数 + 引用环境

实例:
func add()func(y int)int{
	var x int
	return func(y int) int{
		x += y
		return x
	}
}
func main(){
	f := add()
	r := f(10)
	fmt.Print(r)
	fmt.Print(f(20))
}

4.2.9 递归

1. 递归就说自己调用自己
2. 必须先定义函数的推出条件,没有退出条件将进入死循环
3. go语言递归可能会产生一大堆goroutine,也可能出现空间内存溢出问题

实例:
func test(a int ) int {
	if a == 1{ //退出条件
		return 1
	}else{ // 自己调用自己
		return a * test(a-1)
	}
}
func main(){
	i := test(5)
	fmt.Print(i)
}

4.2.10 Defer延迟处理

defer语句会将后面跟随的语句进行延迟处理。

特性:
1. 关键字defer用于注册延迟调用
2. 这些调用直到return前才被执行。因此可以做资源清理
3. 多个defer语句,按先进后出的方式执行
4. defer语句中的变量,defer声明时已经决定

用途:
1.关闭文件句柄
2.锁资源释放
3.数据库连接释放

实例:
func test() {
	fmt.Print("start...")
	defer fmt.Print("start...") // 后执行
	fmt.Print("start...")
	fmt.Print("start...")
	fmt.Print("start...")
	fmt.Print("end...")
}

start...start...start...start...end...start...

4.2.11 init函数

init函数,先于main函数执行,实现包级别的一些初始化操作

特点:
1.init函数先于Main函数自动主席那个,不能被其它函数调用
2.init函数没有输入参数,返回值;
3.每个包可以有多个函数
4.包的每个源文件也可以有多个init函数,这点比较特殊;
5.同一个包的init执行顺序,golang没有明确定义,编程时要注意程序不要以来这个执行顺序
6.不同包的init函数按照包导入的依赖关系决定执行顺序

实例:
func init(){
	fmt.Print("init...")
}
func main(){
	fmt.Print("main...")
}

五、结构体、接口、并发

5.1 类型定义与类型别名

类型定义:type NewType Type // 相当于定义了一个全新的类型
类型别名:type NewType = Type // 没有定义一个新的类型,只是使用另一个别名替换之前的类型

5.2 结构体

go语言没有面向对象概念,但是可以使用结构体实现。
与类型定义,结构体定义和类型定义类似,关键字:struct

语法结构:
type struct_variable_type struct{
    member definition;
    member definition;
    ...
    member definition;
}
type:                结构体定义关键字
struct_variable_type:结构体类型名称
struct:              结构体定义关键字
member definition;:  成员定义

5.2.1 访问结构体成员

实例:
type Person struct{ // 自定义了一个类型,类似int
	id int
	name string
	age  int
	email string
}

func main(){ // 访问结构体成员
	var tom Person // 声明是结构体类型的,类似var i int
	tom.id = 1
	tom.age = 18
	tom.name = "tom"
	fmt.Print(tom.id)
	fmt.Print(tom.age)
	fmt.Print(tom.name)
}

5.2.2 匿名结构体

func main(){
	var tom struct{
		id int
		name string
		age int
	}
	tom.id = 102
	tom.age = 18
	tom.name = "tom"
	fmt.Printf("tom:%v",tom)
}

5.2.3 结构体初始化

func main(){
	type Person struct{
		id int
		name string
		age  int
		email string
	}
	var tom Person
	tom = Person{
		id: 101,
		name: "tom",
		age: 20,
		email: "[email protected]",
	}
	fmt.Printf("%v",tom)
}

5.2.4 结构体指针

func test2(){
	type Person struct{
		id int
		name string
		age  int
	}
	tom := Person{
		id: 101,
		name: "tom",
		age: 20,
	}
	var p_person *Person
	p_person = &tom
	fmt.Printf("tom:%v\n",tom)
	fmt.Printf("p_person:%v\n",p_person)
	fmt.Printf("p_person:%v\n",*p_person)
}

new关键字创建结构体指针:
func test2(){
	type Person struct{
		id int
		name string
		age  int
	}
	var tom = new(Person)
	fmt.Printf("tom:%v\n",tom)
}

5.2.5 结构体作为函数参数

go结构体可以像普通变量一样,作为函数的参数,传递给函数:
1.直接传递结构体,函数内部不会改变外面结构体内容
2.传递结构体指针,函数内部能够改变外部结构体内容

直接传递结构体:
type Person struct{
	id int
	name string
}
func showPerson(per Person){
	per.id = 101
	per.name = "kite"
	fmt.Printf("per:%v",per)
}
func main(){
	showPerson(Person{})
}

指针传递结构体:
type Person struct{
	id int
	name string
}
func showPerson(per *Person){
	per.id = 101
	per.name = "kite"
	fmt.Printf("per:%v",per)
}
func main(){
	tom := Person{
		id : 100,
		name : "tom",
	}
	per := &tom
	showPerson(per)
}

5.2.6 结构体嵌套

func main(){
	type Dog struct{
		name string
		age int
		color string
	}
	type Person struct{
		dog Dog
		name  string
		age int
	}
	dog := Dog{
		name:"hellO",
		age:2,
		color:"black",
	}
	per := Person{
		dog : dog,
		name:"tom",
		age:20,
	}
	fmt.Printf("per:%v",per)
}

通过.来访问:
fmt.Printf("per:%v",per.dog.name)

5.3 方法

5.3.1 方法语言

go语言没有面向对象方法,没有类对象概念,但可以使用结构体模拟这些特性。
go方法是一种特殊函数,定义于struct智商(与struct关联、绑定),被称为struct的接受者:方法就是有接收者的函数
语法格式:
type mytype struct{}
func (recv mytype) my_method(para) return_type{}
func (recv *mytype) my_method(para) return_type{}

实例:
type Person struct{
	name string
}
//(per Person)接收者receiver
func (per Person) eat(){
	fmt.Println("%v,eat...",per.name)
}
func (per Person) sleep(){
	fmt.Println("%v,sleep",per.name)
}
type customer struct{
	name string
}
func (customer customer) login(){
	fmt.Println("%v,login",customer.name)
}
func main(){
	per := Person{
		name:"tom",
	}
	per.eat()
	per.sleep()
	pas := customer{
		name:"tom",
	}
	pas.login()
}

5.3.2 方法接收者类型

结构体实例,有值类型和指针类型,方法接收者是结构体,也有值类型和指针类型。区别就说接收者是否复制结构体副本。值类型复制,指针类型不复制。
值类型结构体和指针类型结构体

实例:
func main(){
	type Person struct{
		name string
	}
	p1 := Person{
		name:"tom",
	}
	p2 := &Person{
		name:"tom",
	}
	fmt.Printf("p1:%T\n",p1)
	fmt.Printf("p2:%T\n",p2)
}

5.4 接口

go语言的接口,是一种新的类型定义,把所有具有共性的方法定义在一起,任何其它类型只要实现了这些方法就是实现了这个接口。

语法格式:
/* 定义接口 */
type interface_name interface{
    method_name1 [return_type]
    ...
    methond_namen [return_type]
}
/* 定义结构体 */
type struct_name struct{
    /* variables */
}
/* 实现接口方法 */
func (struct_name_variable struct_name) method_name1() [return_type]{
    /* 方法实现 */
}
...
func (struct_name_variable struct_name) method_namen() [return_type]{
    /* 方法实现 */
}

实例:
// writer reader
type USB interface{ // 接口定义
	read()
	write()
}
type Computer struct{ // 定义结构体
	name string
}
func (c Computer) read(){ // 方法
	fmt.Printf("c.name: %v\n", c.name)
	fmt.Println("read..")
}
func (c Computer) write(){
	fmt.Printf("c.name: %v\n", c.name)
	fmt.Println("write...")
}
func main(){
	c := Computer{ // 定义
		name:"tom",
	}
	c.read() //调用接口
	c.write()
}

5.4.1 接口之类型接收者和指针类型接收者

type Pet interface{
	eat(string) string
}
type Dog struct{
	name string
}
func (dog Dog) eat(name string) string{
	dog.name = "hello..."
	fmt.Printf("name: %v\n", name)
	return "wow"
}
func main(){
	dog := &Dog{
		name:"hello...",
	}
	s := dog.eat("no")
	fmt.Printf("s:%v\n",s)
	fmt.Printf("dog: %v\n", dog)
}

5.4.2 一个类型实现多个接口

一个类型实现多个接口:
(有一个player接口可以播放音乐,有一个Vieo接口可以播放视频,一个手机实现两个接口,可以放音乐和视频

多个类型实现一个接口)
type Player interface{
	playMusic()
}
type Video interface{
	playVideo()
}
type Mobile struct{}
func (m Mobile) playMusic(){
	fmt.Printf("play music")
}
func (m Mobile) playVideo(){
	fmt.Printf("play video")
}
func main(){
	m := Mobile{}
	m.playMusic()
	m.playVideo()
}

5.4.3 一个接口实现多个类型

多个类型实现一个接口:
type Pet interface{
   eat()
}
type Dog struct{

}
type Cat struct{

}
func (dog Dog) eat(){
   fmt.Println("dog eat...")
}
func (cat Cat) eat(){
   fmt.Println("cat eat...")
}
func main(){
   dog := Dog{}
   dog.eat()
   cat := Cat{}
   cat.eat()
}

5.4.4 接口嵌套

飞鱼:既能飞又能游
飞接口:
type Flayer interface{
    fly()
}
游接口:
type swimmer interface{
    swim()
}

整合实例:
type Fly interface{
	fly()
}
type Swim interface{
	swim()
}
// 接口组合
type Flyfish interface{
    Fly
    Swim
}
type Fish struct{}
func (fish Fish) swim(){
	fmt.Println("swim")
}
func (fish Fish) fly(){
	fmt.Println("fly")
}
func main(){
	var ff Flyfish
	ff = Fish{}
	ff.fly()
	ff.swim()
}

5.4.5 接口实现OCP设计原则

“开-闭”原则为OCP原则。

实例:
type Per interface{ //接口创建
	eat()
	sleep()
}
type Dog struct{} //接收者创建dog
type Cat struct{} //接收者创建cat
func (dog Dog) eat(){ // Dog 实现per接口
	fmt.Println("dog eat...")
}
func (dog Dog) sleep(){
	fmt.Println("dog sleep...")
}
func (cat Cat) eat(){ // cat 实现per接口
	fmt.Println("cat eat...")
}
func (cat Cat) sleep(){
	fmt.Println("cat sleep...")
}
type Person struct{}
func (person Person) care(pet Per){ // Per 既可以传递Dog也可以传递Cat
	pet.eat()
	pet.sleep()
}
func main(){
	dog := Dog{}
	cat := Cat{}
	person := Person{}
	person.care(dog)
	person.care(cat)
}

5.5 一些用法

5.5.1 结构体和函数绑定实现OOP

golang无面向对象概念,没有封装概念,但可以通过结构体struct和函数绑定实现OOP的属性和方法等特性。接收者receiver方法。
例如:定义一个Person类,有name和age属性,有eat/sleep/work方法
实例:
type Person struct{
	name string
	age int
}
func (per Person) eat(){
	fmt.Println("eat...")
}
func (per Person) sleep(){
	fmt.Println("sleep...")
}
func (per Person) work(){
	fmt.Println("work...")
}
func main(){
	per := Person{
		name:"tom",
		age: 20,
	}
	fmt.Print(per)
	per.eat()
	per.sleep()
	per.work()
}

5.5.2 继承

结构体嵌套实现继承

实例:
type Animal struct{
	name string
	age int
}
func (a Animal) eat(){
	fmt.Println("eat...")
}
func (a Animal) sleep(){
	fmt.Println("sleep...")
}
type Dog struct{
	a Animal // 理解为继承
	color string
}
type Cat struct{
	a Animal
	bbb string
}
func main(){
	dog := Dog{
		Animal{name:"hello",age:2,},
		"blue",
	}
	dog.a.eat()
	dog.a.sleep()
	fmt.Printf("dog.color: %v\n", dog.color)
	fmt.Printf("dog.a.age: %v\n", dog.a.age)
}

5.5.3 构造函数

使用函数来模拟构造函数功能

实例:
type Person struct{
	name string
	age int
}
func NewPerson(name string,age int) (*Person ,error){
	if name==""{
		return nil,fmt.Errorf("name = null")
	}
	if age<0 {
		return nil,fmt.Errorf("age = null")
	}
	return &Person{name:"tom",age:20}, nil
}
func main(){
	per,err := NewPerson("tom",20)
	if err == nil{
		fmt.Printf("per:%v\n",per)
	}
}

5.6 并发

5.6.1 golang包

包区分命名空间(一个文件夹中不能有两个同名文件),go中创建一个包,一般是创建一个文件夹,在该文件夹里里面的go文件中,使用package关键字声明包名称,通常文件夹名称和包名称相同,并且在同一个文件下main只有一个包。

创建包:
package dao //声明一个包名为dao
import "fmt" //调用fmt模块
func test1(){
    fmt.Print("test")
}

导入包: (要使用某个包下面的变量或方法,需要导入该包,导入包时,要导入从GOPATH开始的包路径,例如,在service.go中导入dao包)
必须要有的三个文件夹:/src /pkg /bin
package main
import "dao" //调用名为dao的包
func test(){
    fmt.Print("test package")
}

5.6.2 包管理工具 go module

go modules用来管理模块中包的依赖关系。

go mod 使用方法:
* 初始化模块: go mod init <项目模块名称> // 必须使用
* 依赖关系处理,根据go.mod文件: go mod tidy // 下载一些依赖
* 将依赖包复制到项目下的vendor目录: go mod vendor
* 显示依赖关系: go list -m all
* 显示详细依赖关系: go list -m -json all
* 下载依赖: go mod download [path@version]

项目结构:
pro/dao/
        --user_dao.go
pro/service/
        --customer_service.go
        --user_service.go
pro/utils/
        --utils.go
pro/
        --main.go

使用流程:
1.先在main.go目录下进行初始化 go mod init <项目名称文件夹>
2.在需要导入的包,例如service包,先进入cd pro/service/包中,用go build来进行导入包使用
3.在main.go中就可以使用Import{"pro/service"}来进行调用方法

/pro/service/user_service.go:
package service
import{
    "fmt"
}
func TestService(){
    fmt.Print("hello")
}

/pro/main.go:
package main
import{
    "fmt"
    "pro/service"
}
func main(){
    fmt.Println("wow")
    service.TestService()
}

5.6.3 并发编程之协程

Goroutines:并发处理操作一种方式。
使用方法: go tesk()

实例:
func show(msg string){
	for i:=0; i<5; i++{
		fmt.Printf("msg:%v",msg)
		time.Sleep(time.Millisecond * 100)
	}
}
func main(){ // 交替执行
	go show("1") //启动了一个协程
	show("2") 
}

5.6.4 并发编程之通道

用于在goroutine之间共享数据,当执行goroutine之间共享资源或数据,通道充当goroutine之间的管道并提供机制来保证同步交换。
两种类型通道:
无缓冲通道:用于执行goroutine之间的同步通信。
缓冲通道:用于执行异步通信。
区别:无缓冲通道保证在发送和接受发生的瞬间执行两个goroutine之间的交换。缓冲通道没有这样的保证。

通道由make函数创建,该函数指定chan关键字和通道的元素类型。
语法:
Unbuffered := make(chan int) // 整形无缓冲通道
buffered := make(chan int, 10) // 整形有缓冲通道

使用内置函数Make创建无缓冲和缓冲通道,make第一个参数需要关键字chan,然后是通道允许交换的数据类型。
将值发送到通道代码块需要使用 <- 运算符:
语法:
goroutine1 := make(chan string,5) // 字符串缓冲通道
goroutine1 <- "Australia" // 通过通道发送字符串

通道接收值得代码块:<- 运算符附加到通道变量(goroutine1)的左侧,以接收来自通道的值
语法:
data := <-goroutine1 // 从通道接收字符串

通道的发送和接受特性:
1.对于同一个通道,发送操作之间是互斥的,接受操作之间也是互斥的
2.发送操作和接受操作中对元素值的处理是不可分割的
3.发送操作在完全完成之前会被堵塞。接受操作也是如此

实例:
package main
import (
	"fmt"
	"time"
	"math/rand"
)
var values = make (chan int) // 创建int类型通道,只能传入int类型值
func send(){
	rand.Seed(time.Now().UnixNano())
	value := rand.Intn(10)
	fmt.Printf("value: %v\n", value)
	// time.Sleep(time.Second * 5)
	values <- value
}
func main(){ // 从通道接收值
	defer close(values)
	go send()
	fmt.Println("wait...")
	value := <- values
	fmt.Printf("value: %v\n", value)
	fmt.Println("end...")
}

5.6.5 并发编程之WaitGroup实现同步

WaitGroup:还没执行完,稍等一会。
实例:
import (
	"fmt"
	"sync"
)
var wp sync.WaitGroup
func msg(i int){
	defer wp.Done()  // defer wp.add.(-1) 两条语句一样 goroutine结束就登记-1
	fmt.Printf("i: %v\n", i)
}
func main(){
	for i:=0; i<10; i++{
		go msg(i)	//启动一个协程来执行
		wp.Add(1) //启动一个goroutine就登记+1
	}
	wp.Wait() //等待所有登记的goroutine都结束
	// 主协程
	fmt.Println("end...")
}

5.6.6 并发编程之runtime

runtime:包内定义了一些协程管理相关的api
===================================================================================
runtime.Gosched():让出CPU时间片,重新等待安排任务
实例:
func show(msg string){
	for i:=0; i<2; i++{
		fmt.Printf("msg: %v\n", msg)
	}
}
func main(){
	go show("java") //子协程运行
	for i:=0; i<2; i++{
		runtime.Gosched() // 我有权力执行任务了,让给其它子协程来执行
		fmt.Printf("golang:golang")
	}
	fmt.Println("end...")
}
===================================================================================
runtime.Goexit():退出当前协程
import (
	"fmt"
	"time"
	"runtime"
)
func show(){
	for i:=0; i<10; i++{
		fmt.Printf("i: %v\n", i)
		if i>=5{
			runtime.Goexit() // 子线程大于5停止退出
		}
	}
}
func main(){
	go show()
	time.Sleep(time.Second)
}
===================================================================================
runtime.GOMAXPROCS:CPU核心线程设置
实例:
func a(){
	for i := 0; i < 10; i++ {
		fmt.Printf("a: %v\n", a)
		time.Sleep(time.Millisecond * 100) // 睡一百毫秒
	}
}
func b(){
	for i := 0; i < 10; i++ {
		fmt.Printf("b: %v\n", b)
	}
}
func main(){
	fmt.Println("runtime.NumCPU():%v\n",runtime.NumCPU())
	runtime.GOMAXPROCS(1)
	go a()
	go b()
	time.Sleep(time.Second)
}

5.6.7 并发编程之Mutex互斥锁实现同步

snyc.Mutex: 通过互斥锁来实现同步操作
实例:
import (
	"fmt"
	"sync"
	"time"
)
var i int = 100
var wg sync.WaitGroup
var lock sync.Mutex
func add(){
	defer wg.Done()
	lock.Lock()
	i += 1
	fmt.Printf("i++: %v\n", i)
	time.Sleep(time.Millisecond *10)
	lock.Unlock()
}
func sub(){
	lock.Lock()
	defer wg.Done()
	i -= 1
	fmt.Printf("i--: %v\n", i)
	time.Sleep(time.Millisecond *2)
	lock.Unlock()
}
func main(){
	for i := 0; i < 100; i++ {
		wg.Add(1)
		add()
		go add()
		sub()
		go sub()
	}
	wg.Wait()
	fmt.Printf("end i: %v\n", i)
}

5.6.8 并发编程之channel遍历

方法1:for循环+if判断
var c = make(chan int)
func main(){
	go func(){
		for i := 0; i < 5; i++ {
			c <- i
		}
		close(c) // 一定要关闭通道,不然会造成死锁产生错误
	}()
	for {
		v, ok := <-c
		if ok {
			fmt.Printf("v: %v\n", v)
		}else{
			break
		}
	}
}

===================================================================================

方法2:for range
var c = make(chan int)
func main(){
	go func(){
		for i := 0; i < 5; i++ {
			c <- i
		}
		close(c) // 一定要关闭通道,不然会造成死锁产生错误
	}()
	for v := range c{
		fmt.Printf("v: %v\n", v)
	}
}

5.6.9 并发编程之select

1.Select是go中的控制结构。类似于switch语句,用于处理异步IO操作。select会监听case语句中channel的读写操作,当case中channel读写操作为非阻塞状态(即能读写)时,就会触发响应的动作。
select语句必须是一个channel操作
select中的default子句总是可运行
2.如果有多个case都可以运行,select会随机公平地选出一个执行,其它不会执行。
3.如果没有可运行地case语句,且有default语句,就会执行default动作
4.如果没有可运行地case语句,且没有default语句,select将阻塞,直到某个case通信可以运行。

实例:
import (
	"fmt"
	"time"
)
var chanInt = make(chan int,0)
var chanStr = make(chan string)
func main(){
	go func(){
		chanInt <- 100
		chanStr <- "hell0"
		defer close(chanInt)
		defer close(chanStr)
	}()
	for{
		select {
		case r:= <- chanInt:
			fmt.Printf("r: %v\n", r)
		case r:= <- chanStr:
			fmt.Printf("r: %v\n", r)
		default:
			fmt.Println("default...")
		}
		time.Sleep(time.Second)
	}
}

5.6.10 并发编程之Timer

Timer:定时器,实现定时操作,器内部也是通过channel来实现。

实例:
func main(){
	//方法一:
	timer := time.NewTimer(time.Second *2)
	fmt.Printf("time.now():%v\n",time.Now())
	t1 := <- timer.C // 阻塞的,指定时间到了
	fmt.Printf("t2: %v\n", t1)
===================================================================================
	//方法二:
	timer1 := time.NewTimer(time.Second*2)
	<-timer1.C
	fmt.Printf("timer: %v\n", timer)
===================================================================================
	//方法三: 如果只是单纯等待,用sleep.time实现
	time.Sleep(time.Second)
===================================================================================
	//方法四
	<-time.After(time.Second *2)
	fmt.Printf("...")
===================================================================================
	//方法五
	timer2 := time.NewTimer(time.Second)
	go func(){
		<- timer2.C
		fmt.Println("...")
	}()
	s := timer.Stop()
	if s{
		fmt.Println("stop...")
	}
	time.Sleep(time.Second *3)
	fmt.Println("func...")
}
===================================================================================

5.6.11 并发编程之Ticker

Timer只执行一次,Ticker可以周期执行。

实例:
func main(){
	ticker := time.NewTicker(time.Second) // ticker周期性执行,两秒
	counter := 1 //设置一个数为1
	for _ = range ticker.C { //循环遍历
		fmt.Println("ticker...") // 两秒周期输出ticker循环不停
		counter++  //利用设置数来使周期的ticker停止
		if counter <=5{
			ticker.Stop()
			break
		}
	}
}

5.6.12 并发编程之原子变量的引入

import (
	"fmt"
	"sync/atomic" //原子操作包,跟互斥锁类似
	"time"
)
var i int32 = 100
func add(){
	atomic.AddInt32(&i,1)
}
func sub(){
	atomic.AddInt32(&i,-1)
}
func main(){
	for i := 0; i < 100; i++ {
		go add()
		go sub()
	}
	time.Sleep(time.Second *2)
	fmt.Printf("i: %v\n", i)
}

5.6.13 并发编程之原子操作详解

atomic提供原子操作能够确保任一时刻只有一个goroutine对变量进行操作,善用atomic能够避免程序中出现大量的锁操作。
atomic常见操作:
* 增减
* 载入
* 比较并交换cas
* 交换
* 存储

atomic.AddInt32(&i,1) // 增减
fmt.Printf("i: %v\n",i)
atomic.AddInt32(&i,-1)
fmt.Printf("i: %v\n",i)

atomic.LoadInt32(&i) // read
fmt.Printf("i: %v\n", i) 

atomic.StoreInt32(&i,200) // write
fmt.Printf("i: %v\n", i)

b := atomic.CompareAndSwapInt32(&i,100, 300) //cas
fmt.Printf("b: %v\n", b) // 如果有问题返回flase
fmt.Printf("i: %v\n", i)

六、标准库

6.1 标准库OS模块

os标准库实现平台(操作系统)无关的编程接口

6.1.1 文件目录操作

// 创建文件
func createFile(){
	f, err := os.Create("a.txt")
	if err != nil{
		fmt.Printf("err: %v\n", err)
	}else{
		fmt.Printf("f.Name():%v\n",f.Name())
	}
}
// 创建目录
func makeDir(){
	err := os.Mkdir("test",os.ModePerm) // 创建test目录,os.ModePerm赋予权限
	if err != nil{
		fmt.Printf("err: %v\n", err)
	}
	err2 := os.MkdirAll("a/b/c",os.ModePerm) // 创建多个目录
	if err2 != nil{
		fmt.Printf("err2: %v\n", err2)
	}
}
// 删除目录
func deletDir(){
	err := os.Remove("test.txt") //删除test文件
	if err != nil{
		fmt.Printf("err: %v\n", err)
	}
	os.RemoveAll("a") //删除整个目录
	if err != nil{
		fmt.Printf("err: %v\n", err)
	}
}
// 获取工作目录
func wd(){
	dir, _ := os.Getwd() //获取工作目录
	fmt.Printf("dir: %v\n", dir)

	os.Chdir("d:\"") //修改工作目录

	s := os.TempDir() // 临时目录
	fmt.Printf("s: %v\n", s)
}
// 重命名文件
func rename(){
	err := os.Rename("test.txt","test2.txt") //将test.txt更名为test2.txt
	if err != nil {
		fmt.Printf("err: %v\n", err)
	}
}
// 读文件
func readFile(){
	b, err := os.ReadFile("test2.txt")
	if err != nil{
		fmt.Printf("err: %v\n", err)
	}else{
		fmt.Printf("b: %v\n", string(b[:]))
	}
}
// 写文件
func writeFile(){
	s := "hello world"
	os.WriteFile("test2.txt",[]byte(s),os.ModePerm)
}

6.1.2 文件读操作

// 打开关闭软件
func openCloseFile(){
// 只读
	f, _ := os.Open("test.txt")
	fmt.Printf("f.Name():%v\n",f.Name())
	f.Close()
// 读写或创建
	f2, _ := os.OpenFile("test2.txt",os.O_RDWR|os.O_CREATE,0755)
	fmt.Printf("f2.Name():%v\n",f2.Name())
	err := f.Close()
	fmt.Printf("err: %v\n", err)
	err2 := f2.Close()
	fmt.Printf("err2: %v\n", err2)
}
// 创建文件
func createFile(){
// 等价于,OpenFile(name,O_RDWR|O_CREATE|O_TRUNC,0666)
	f,_ := os.Create("test3.txt")
	fmt.Printf("f.Name(): %v\n", f.Name())
// 第一个参数 目录默认,Temp 第二个参数 文件名前缀
	f2,_ := os.CreateTemp("","temp")
	fmt.Printf("f2.Name(): %v\n", f2.Name())
}
// 读取文件
func readOps(){
// 循环读取
	f,_ := os.Open("a.txt") // 打开文件
	for {
		buf := make([]byte,6) // 缓冲区
		n,err := f.Read(buf) // 读到缓冲区里
		fmt.Printf(string(buf)) 
		fmt.Printf("n: %v\n", n)
		if err == io.EOF{ // 读取字节中如果有EOF为空的字样,则跳出循环
			break
		}
	}
	f.Close()
// 从第几个开始读多少字节操作
	buf := make([]byte,10)
	f2,_ := os.Open("a.txt")
	n,_ := f2.ReadAt(buf,5) // 从第五开始读十个
	fmt.Printf("n: %v\n", n)
	fmt.Printf("buf: %v\n", string(buf))
	f.Close()
// 遍历目录
	de,_ := os.ReadDir("a")
	for _, v := range de{
		fmt.Printf("v.IsDir():%V\n",v.IsDir())
		fmt.Printf("v.Name():%V\n",v.Name())
	}
// 定位读取
	f3,_ := os.Open("a.txt")
	f3.Seek(3,0)
	buf2 := make([]byte,5)
	n2,_ := f3.Read(buf)
	fmt.Printf("n: %v\n", n2)
	fmt.Printf("buf: %v\n", string(buf2))
	f.Close()
}

6.1.3 文件写操作

// 写入文件操作
func write() {
//f,_ := os.OpenFile("test.go") // 打开文件,写入但并不清除原来的内容弄个
	f,_ := os.OpenFile("a.txt",os.O_RDWR|os.O_TRUNC,0755) //清除原来的内容并把写入内容替换
	f.Write([]byte("hello world")) // 写入的内容"hello world"
	f.Close()
}
// 定位写入文件
func writeString(){
	f,_ := os.OpenFile("a.txt",os.O_RDWR|os.O_TRUNC,0755)
	f.WriteAt([]byte("abc"),3) // 在文件第三个字节中插入abc
	f.Close()
}

6.1.4 进程相关操作

// 获取当前正在运行的进程id
	fmt.Printf("os.Getpid():%v\n",os.Getpid())
// 父id
	fmt.Printf("os.Getpidd():%v\n",os.Getppid())
// 设置新进程的属性
	attr := &os.ProcAttr{
//files指定新进程继承的活动文件对象
//前三个分别为,标准输入、标准输出、标准错误输出
		Files:[]*os.File{os.Stdin, os.Stdout, os.Stderr},
//新进程的环境变啊零
		Env: os.Environ(),
	}
// 开启一个新进程
	p, err := os.StartProcess("C:\\windows\\System32\\notepad.exe",[]string{"C:\\Windows\\System32\\notepad.exe","D:\\a.txt"},attr)
	if err != nil{
		fmt.Println(err)
	}
	fmt.Println(p)
	fmt.Println("进程ID:",p.Pid)
// 通过线程ID查找进程
	p2, _ := os.FindProcess(p.Pid)
	fmt.Println(p2)
//等待10秒,执行函数
	time.AfterFunc(time.Second*10,func(){
//向p进程发送退出信号
		p.Signal(os.Kill)
	})
//等待进程p的退出,返回进程状态
	ps,_ := p.Wait()
	fmt.Println(ps.String())

6.1.5 包和环境变量

// 获得所有环境变量
	s := os.Environ()
	fmt.Printf("s: %v\n", s)
// 获某个环境变量
	s2 := os.Getenv("GOPATH")
	fmt.Printf("s2: %v\n", s2)
// 设置环境变量
	os.Setenv("env1","env1")
	s2 = os.Getenv("aaa")
	fmt.Printf("s2: %v\n", s2)
// 查找
	s3,b := os.LookupEnv("env1")
	fmt.Printf("b: %v\n", b)
	fmt.Printf("s3: %v\n", s3)
// 替换
	os.Setenv("NAME","gopher")
	os.Setenv("BURROW","/usr/gopher")
	fmt.Println(os.ExpandEnv("$NAME lives in ${BURROW},"))
// 清空环境变量
// os.Clearenv()

6.2 标准库IO模块

Input Output
IO操作封装如下几个包:
* io为IO原语(I/O primitives)提供基本的接口
* io/ioutil封装一些试用的I/O函数
* fmt实现格式化I/O,类似C语言中的printf和scanf
* bufio实现带缓冲I/O

io接口:Reader和Writer接口

Reader接口:
type Reader interface{
    Read(p []byte) (n int,err error)
}

Writer接口:
type Writer interface{
    Write(p []byte) (n int,err error)
}

哪些类型实现Reader和Writer接口
* os.File同时实现了io.Reader和io.Writer
* strings.Reader实现了io.Reader
* bufio.Reader/Writer分别实现io.Reader和io.Writer
* bytes.Buffer同时实现
* bytes.Reader实现io.Reader
* compress/gzip.Reader/Writer分别实现
* crypto/cipher.StreamReader/StreamWriter分别实现
* crypto/tls.Conn同时实现
* encoding/csv.Reader/Writer分别实现

6.2.1 ioutil包

io/ioutil:封装一些实用的I/O函数

ReadALL:读取数据,返回读到的字节slice
ReadDir:读取一个目录,返回目录入口数组[]os.FileInfo
ReadFile:读一个文件,返回文件内容(字节slice)
WriteFile:根据文件路径,写入字节slice
TempDir:在一个目录中创建指定前缀名的临时目录,返回新临时目录的路径
TempFile:在一个目录创建指定前缀名的临时问价,返回os.File

实例:
func readall(){ // 读文件
	// r := strings.NewReader("hello world!")
	f,_ := os.Open("a.txt") //File实现了Reader接口
	defer f.Close()
	b, err := ioutil.ReadAll(f)
	if err != nil{
		fmt.Printf("err: %v\n", err)
	}
	fmt.Printf("b: %v\n", string(b))
}
func testReadDir(){ // 读目录
	fi, _ := ioutil.ReadDir(".")
	for _, v := range fi{
		fmt.Printf("v: %v\n", v.Name())
	}
}
func testReadFile(){ // 读文件
	b,_ := ioutil.ReadFile("a.txt")
	fmt.Printf("string(b): %v\n", string(b))
}
func testWriteFile(){ // 写文件
	ioutil.WriteFile("a.txt",[]byte("hello world"),0664) // 0664 给权限,将hello world写入a.txt
}
func testTempFile(){ // 创建临时文件,会在Temp目录下临时创建文件
	content := []byte("temporary file's content")
	tmpfile,err := ioutil.TempFile("","example")
	if err != nil{
		log.Fatal(err)
	}
	fmt.Printf("tmpfile.Name(): %v\n", tmpfile.Name())
	if _, err := tmpfile.Write(content); err != nil{
		log.Fatal(err)
	}
	if err := tmpfile.Close(); err != nil{
		log.Fatal(err)
	}
}

6.3 标准库bufio

bufio:实现缓冲I/O,包装一个io.Reader或io.Writer接口对象,创建另一个也实现了该接口,且同时还提供了缓冲和一些文本I/O的帮助函数对象。

实例一:
r := strings.NewReader("hello world")
r2 := bufio.NewReader(r) //通过bufio.NewReader封装一个Newreader
s,_ := r2.ReadString('\n')
fmt.Printf("s: %v\n", s)

实例二:func (*Reader)Read
s := strings.NewReader("ABCDEFGHIJKLMNOPQRSTUVWSYZ1234567890")
br := bufio.NewReader(s)
p := make([]byte,10)
for {
    n,err := br.Read(p)
    if err == io.EOF{
        break
    }else{
        fmt.Printf("string(p):%v\n",string(p[0:n]))
    }
}

示例三:
func(*Reader)ReadByte:
func (b *Reader) ReadByte()(c byte,err error): ReadByte读取并返回一个字节。如果没有可用数据会返回错误。
func(*Reader)UnreadByte:
func (b *Reader) UnreadByte() error: UnreadByte吐出最近一次读取操作读取的最后一个字节并且只能吐出最后一个。
	s := strings.NewReader("ABCDEFG")
	br := bufio.NewReader(s)
	c,_ := br.ReadByte()
	fmt.Printf("c: %v\n", c)
	c,_ = br.ReadByte()
	fmt.Printf("c: %v\n", c)
	br.UnreadByte()
	c,_ = br.ReadByte()
	fmt.Printf("c: %v\n", c)

实例四:
func (b *Reader)ReadRune():readRune读取一个utf-8编码的unicode码值,返回该码值,其编码长度和可能的错误。如果utf-8编码非法,读取位置只移动1字节,返回U+FFFD,返回值size为1而err为nil,如果没有可用数据会返回错误。
语法:func (b *Reader) ReadRune()(r rune, size int,err error)

func (b *Reader)UnreadRune():UnreadRune吐出最近一次ReadRune调用读取的unicode码值,如果最近一次读取不是调用的ReadRune,会返回错误。
语法:func (b *Reader) UnreadRune() error

s :=  strings.NewReader("hello world")
br := bufio.NewReader(s)
c,size,_ := br.ReadRune()
fmt.Printf("%c %v\n",c,size)

c,size,_ = br.ReadRune()
fmt.Printf("%c %v\n",c,size)

br.UnreadRune()
c,size,_ = br.ReadRune()
fmt.Printf("%c %v\n",c,size)

示例五:
func(*Reader)ReadLine:如果一行特别多,超过缓冲区大小,会把后面省略掉
语法:func (b *Reader) ReadLine()(line []byte,isPrefix bool,err error)

s := strings.NewReader("abc\NdEF\r\nGHI\r\nGHI")
br := bufio.NewReader(s)
 
w,isPrefix,_ := br.ReadLine()
fmt.Printf("%q %v\n",w,isPrefix)

w,isPrefix,_ = br.ReadLine()
fmt.Printf("%q %v\n",w,isPrefix)

w,isPrefix,_ = br.ReadLine()
fmt.Printf("%q %v\n",w,isPrefix)

w,isPrefix,_ = br.ReadLine()
fmt.Printf("%q %v\n",w,isPrefix)

实例六:
func (*Reader)ReadSlice:读取一个切片
语法:func (b *Reader) ReadSlice(delim byte)(line []byte,err error)
s := strings.NewReader("ABC,DEF,GHI,JKL")
br := bufio.NewReader(s)
w,_ := br.ReadSlice(',')
fmt.Printf("%q\n",w)
w,_ = br.ReadSlice(',')
fmt.Printf("%q\n",w)
w,_ = br.ReadSlice(',')
fmt.Printf("%q\n",w)

实例七:
func (*Reader) ReadString:读取一个字符串
func (b *Reader) ReadString(delim byte)(line string,err error)
s := strings.NewReader("ABC DEF GHI JKL")
br := bufio.NewReader(s)
w,_ := br.ReadString('')
fmt.Printf("%q\n",w)
w,_ = br.ReadString('')
fmt.Printf("%q\n",w)
w,_ = br.ReadString('')
fmt.Printf("%q\n",w)

实例八:
func (*Reader)WriteTo:WriteTo方法实现了io.WriterTo接口
func (b *Reader) WriteTo(w io.Writer)(n int64,err error)

s := strings.NewReader("ABCDEFGHIJKLMN")
br := bufio.NewReader(s)
b := bytes.NewBuffer(make([]byte,0))
br.WriteTo(b) // 写到缓冲区
fmt.Printf("%s\n",b)

6.3.1 写操作

type Writer:Writer实现了为io.Writer接口对象提供缓冲,如果在向一个Writer类型值写入时遇到了错误,该对象将不再接受任何数据,且所有写操作都会返回该错误。
实例:
type Writer struct{
    err error
    buf []byte
    n int
    wr io.Writer
}
func NewWriter: NewWriter创建一个具有默认大小缓冲、写入w的*Writer。NewWriter相当于NewWriterSize(wr,4096)
func NewWriter(w io.Writer) *Writer
func NewWriterSize: 创建一个具有最少有size尺寸的缓冲、写入w的Writer。如果参数w已经是一个具有足够大缓冲的Writer类型值,会返回w。
func NewWriter(w io.Writer,size int) *Writer
func (*Writer)Reset: Reset丢弃缓冲中的数据,清除任何错误,将b重设为将其输出写入w
func (b *Writer) Reset(w io.Writer)
    f,_ := os.OpenFile("a.txt",os.O_RDWR,0777)
    defer f.Close()
    w := bufio.NewWriter(f) // 即可以writer也可以reader
    w.WriteString("hello world!")
    w.Flush() // 刷新缓冲区
func (b *Write)Bufferd() int: Buffered返回缓冲中已使用的字节数
	b := bytes.NewBuffer(make([]byte,0))
	bw := bufio.NewWriter(b)
	bw.WriteString("123456789")
	c := bytes.NewBuffer(make([]byte,0))
	bw.Reset(c)
	bw.WriteString("456")
	bw.Flush()
	fmt.Printf("b: %v\n", b)
	fmt.Printf("c: %v\n", c)
func (b *Writer)Available() int: Available返回缓冲中还有多少字节未使用
func (b *Writer)Write(p []byte)(nn int,err error): 将p内容写入缓冲区,返回写入的字节数。如果返回值nn<len(p),会返回错误。
func (b *Writer)WriteString(s string)(int,error):写入字符串,返回字节数。
func (b *Writer)WriteByte(c byte)error:WriteByte写入单个字节
func (b *Writer)WriteRune(r rune)(size int,err error):写入一个unicode码值(utf-8编码),返回写入的字节数和可能的错误。
func (b *Writer)Flush()error:Flush方法将缓冲中的数据写入下层io.Writer接口
func (b *Writer)ReadForm(r io.Reader)(n int64,err error):ReadFrom实现io.ReaderFrom接口
	b := bytes.NewBuffer(make([]byte,0))
	bw := bufio.NewWriter(b)
	fmt.Println(bw.Available()) //4096
	fmt.Println(bw.Buffered()) // 0
	bw.WriteString("abcdefghijklmn")
	fmt.Println(bw.Available())
	fmt.Println(bw.Buffered())
	bw.Flush()  // 刷新缓冲区
	fmt.Println(bw.Available())
	fmt.Printf("%q\n",b)
type ReadWriter struct{ // ReadWriter类型保管了指向Reader和Writer类型的指针,实现了io.ReadWriter接口
    *Reader
    *Writer
}
func NewReadWriter(r *Reader,W *Writer)*ReadWriter:申请创建一个新的、将读写操作分派给r和w的ReadWriter
	b := bytes.NewBuffer(make([]byte,0))
	bw := bufio.NewWriter(b)
	s := strings.NewReader("123")
	br := bufio.NewReader(s)
	rw := bufio.NewReadWriter(br,bw)
	p,_ := rw.ReadString('\n')
	fmt.Println(string(p))
	rw.WriteString("asdf")
	rw.Flush()
	fmt.Println(b)

6.3.2 Scanner包

Scanner类型是提供方便的读取数据接口。
type SplitFunc func(data []byte,atEOF bool)(advance int, token []byte,err error):SplitFunc类型代表用于输出作词法分析的分割函数
func ScanBytes(data []byte,atEOF bool)(advance int,token []byte,err error):ScanBytes是用于Scanner类型的分割函数(符合SplitFunc),本函数会将每个字节作为一个token返回。
func ScanRunes(data []byte,atEOF bool)(advance int,token []byte,err error):本函数将每个utf-8编码的unicode码值作为一个token返回,本函数返回的rune序列和range一个字符串的的输出rune序列相同。
func ScanWords(data []byte,atEOF bool)(advance int,token []byte,err error):会将空白分割片段作为一个token返回。
func ScanLines(data []byte,atEOF bool)(advance int,token []byte,err error):用于scanner类型的分割函数。
type Scanner struct{
    r io.Reader
    split SplitFunc
    maxTokenSize int
    token []byte
    buf []byte
    start int
    end int
    err error
}
func NewScanner(r io.Reader) *Scanner: NewScanner创建并返回一个从r读取数据的Scanner。
func (s *Scanner) Split(split SplitFunc):Split设置该Scanner的分割函数,本方法必须在scan之前调用。
	s := strings.NewReader("ABC DEF GHI JKL")
	bs := bufio.NewScanner(s)
	bs.Split(bufio.ScanWords)
	for bs.Scan(){
		fmt.Println(bs.Text())
	}
func (s *Scanner) Scan() bool:获取当前位置token,并让Scanner的扫描位置移动到下一个token.
	s := strings.NewReader("hello world!")
	bs := bufio.NewScanner(s)
	bs.Split(bufio.ScanBytes)
	for bs.Scan(){
		fmt.Printf("%s",bs.Text())
	}
func (s *Scanner) Bytes()[]byte:返回最近一次Scan调用生成的token。底层数组指向的数据可能会被下一次Scan的调用重写。
	s := strings.NewReader("hello world!")
	bs := bufio.NewScanner(s)
	bs.Split(bufio.ScanRunes)
	for bs.Scan(){
		fmt.Printf("%s",bs.Text())
	}
func (s *Scanner) Text() string:bytes方法返回最近一次Scan带哦用生成的token,会申请创建一个字符串保存token并返回该字符串。
func (s *Scanner) Err() error:返回Scanner遇到的第一个EOF的错误。

6.3 标准库log

log简介:内置的Log包简单实现日志服务,通过调用Log包函数可实现日志打印功能
使用:Log包内有3个系列日志打印函数,print系列、panic系列、fatal系列
print:单纯打印日志
panic:打印日志抛出panic异常
fatal:打印日志,强制结束程序(os.Exit(1),defer函数不会执行)

实例:
	log.Print("my log")
	log.Printf("my log %d",100)
	name := "tom"
	age := 20
	log.Println(name," ",age)

6.3.1 log配置

默认情况下log指挥打印出时间,但是实际情况下我们可能还需要获取文件名,行号等信息,log包提供给我们定制的接口。
log包提供两个标准Log配置的相关方法:
func Flags() int // 返回标准log输出配置
func SetFlags(flag int) // 设置标准log输出配置

flag参数:
const{
    //控制输出日志信息的细节,不饿能控制输出的顺序和格式
    //输出的额日志在每一项后会有一个冒号分隔,例如2009/1/1 0101:01 123123 /a/b/c.go:23:message
    Ldate = 1 << iota //日期
    Ltime  //时间
    Lmicroseconds //微妙级别的时间
    Llongfile //文件全路径名+行号
    Lshorfile //文件名+行号
    LUTC //使用UTC时间
    LstdFlags = Ldate | Ltime //标准logger的初始值
}
示例:
	log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
	i := log.Flags()
	fmt.Printf("i:%v\n",i)
	log.Print("my log...")

日志前缀配置:
log包提供两个日志前缀配置的相关函数:
func Prefix() string //返回日志的前缀配置
func SetPrefix(prefix string) //设置日志前缀
示例:
	log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
	log.SetPrefix("MyLog:")
	f,err := os.OpenFile("a.log",os.O_WRONLY|os.O_APPEND|os.O_CREATE,0664)
	if err != nil{
		log.Fatal("日志文件错误")
	}
	log.SetOutput(f)

自定义logger:
log包中提供了func New(out io.Writer,prefix string,flag int) *Logger函数来实现自定义logger
示例:
var logger *log.Logger
func init(){
    logFile,err := os.OpenFile("a.log",os.O_CREATE|os.O_WRONLY|os.O_APPEND,0644)
    if err != nil{
        log.Panic("打开日志文件异常")
    }
    logger = log.New(logFile,"success",log.Ldate|log.Ltime|log.Lshortfile)
    logger.Println("自定义Logger")
}

6.4 标准库builtin

builtin包提供了类型声明、变量、常量声明,还有一些便利函数,这个包不需要导入,可以直接使用。

常用函数:
append: 切片
func append(slice []Type,elems ...Type)[]Type
slice = append(slice,elem1,elem2) //直接在slice后面添加单个元素,添加元素类型可以和slice相同,也可以不同
slice = append(slice,anotherSlice...) //直接将另一个slice添加到slice后面,但其本质还是将anotherSlice中的元素一个一个添加到slice中,和第一种方法类似
示例:
	s1 := []int{1,2,3}
	i := append(s1,4)
	fmt.Printf("i:%v\n",i)
	s2 := []int{7,8,9}
	i2 := append(s1,s2...)
	fmt.Printf("i2: %v\n", i2)

len: 返回、数组、切片、字符串、通道的长度
示例:
	s1 := "hello world"
	i := len(s1)
	fmt.Printf("i: %v\n", i)
	s2 := []int{1,2,3}
	fmt.Printf("s2: %v\n", len(s2))

Print、Println: 打印输出到控制台
示例:
	name := "tom"
	age := 20
	print(name," ",age,"\n")
	println(name," ",age)

panic: 抛出一个panic异常
示例:
    defer fmt.Println("panic 异常后执行")
    panic("panic 错误...")
    fmt.Println("end...")

new和Make: 
1.make只能用来分配及初始化类型为slice,map,chan的数据;new可以分配任意类型的数据
2.new分配返回的是指针,即类型*T;make返回引用,即T;
3.new分配的空间被清零,make分配后,会进行初始化
示例:
NEW:
	b := new(bool)
	fmt.Printf("b: %v\n", *b)
	i := new(int)
	fmt.Printf("i: %v\n", *i)
	s := new(string)
	fmt.Printf("s: %v\n", *s)
MAKE:
	make([]int,10,100)

6.5 标准库bytes

bytes包提供了对字节切片进行读写操作的一系列函数,字节切片处理的函数比较多分为基本处理函数、比较函数、后缀检查函数、索引函数、分割函数、大小写处理函数和子切片处理函数等。

// Contains : 字符串强转为byte切片
	s := "cllmsy.com"
	b := []byte(s) // 强类型转换
	b1 := []byte("cllmsy.com")
	b2 := []byte("cllmsy.cn")
	b3 := bytes.Contains(b,b1)
	fmt.Printf("%v",b3)
	b3 = bytes.Contains(b,b2)
	fmt.Printf("b3:%v\n",b3)
	strings.Contains("hello world","hello") // 判断是否含有hello

// Count
	s := []byte("helloooooo")
	sep1 := []byte("h")
	sep2 := []byte("l")
	sep3 := []byte("o")
	fmt.Println(bytes.Count(s,sep1)) //1
	fmt.Println(bytes.Count(s,sep2)) //2
	fmt.Println(bytes.Count(s,sep3)) //9
// Repeat 重复
    b = []byte("hi")
    fmt.Println(string(bytes.Repeat(b,1))) //hi
    fmt.Println(string(bytes.Repeat(b,3))) //hihihi
// Replace 替换
	s := []byte("hello,world")
	old := []byte("o")
	news := []byte("ee")
	fmt.Println(string(bytes.Replace(s,old,news,0))) //hello,world
	fmt.Println(string(bytes.Replace(s,old,news,1))) //hellee,world
	fmt.Println(string(bytes.Replace(s,old,news,2))) //hellee,weerld
	fmt.Println(string(bytes.Replace(s,old,news,-1))) //hellee,weerld
// Runes
	s := []byte("hello world")
	r := bytes.Runes(s)
	fmt.Printf("转换前字符长度: %v\n", len(s))
	fmt.Printf("转换后字符长度: %v\n", len(r))
// Join
	s2 := [][]byte{[]byte("hello"),[]byte("world")}
	sep4 := []byte(",")
	fmt.Println(string(bytes.Join(s2,sep4))) //hello,world
	sep5 := []byte("#")
	fmt.Println(string(bytes.Join(s2,sep5))) //hello#world

6.5.1 Reader包

io.Reader实现了 io.Reader, io.ReaderAt, io.WriterTo, io.Seeker, io.ByteScanner, io.RuneScanner接口,Reader是只读,可以seek。

示例:
	data := "123456789"
	re := bytes.NewReader([]byte(data)) //通过[]byte创建reader
	fmt.Println("re len:",re.Len()) //返回未读取部分的长度
	fmt.Println("re size:",re.Size()) //返回底层数据总长度
	
	buf := make([]byte,2)
	for {
		n, err := re.Read(buf) //读取数据
		if err != nil{
			break
		}
		fmt.Println(stirng(buf[:n]))
	}
	re.Seek(0,0) //设置偏移量,因为上面的操作以及修改了读取位置等信息
	for {
		b ,err := re.ReadByte() //一个字节一个字节的读
		if err != nil{
			break
		}
		fmt.Println(string(b))
	}
	re.Seek(0,0)
	off := int64(0)
	for {
		n,err := re.ReadAt(buf,off) //指定偏移量读取
		if err!= nil{
			break
		}
		off += int64(n)
		fmt.Println(off,string(buf[:n]))
	}

6.5.2 Buffer类型

缓冲区是具有读取和写入方法的可变大小的字节缓冲区,buffer的零值是准备使用的空缓冲区

声明一个Buffer的四种方法:
var b bytes.Buffer //直接定义一个buffer变量,不用初始化,直接使用
b := new(bytes.Buffer) //使用new返回buffer变量
b := bytes.NewBuffer(s []byte) //从一个[]byte切片,构造一个buffer
b := bytes.NewBufferString(s string) //从一个string变量,构造一个buffer

示例:
	var b bytes.Buffer
	fmt.Printf("b: %v\n", b)
	var b1 = bytes.NewBufferString("hello")
	fmt.Printf("b1: %v\n", b1)
	var b2 = bytes.NewBuffer([]byte("hello"))
	fmt.Printf("b2: %v\n", b2)

往buffer中写入数据:
b.Write(d []byte) //将切片d写入buffer尾部
b.WriteString(s string) //将字符串s写入buffer尾部
b.WriteByte(c byte) //将字符c写入Buffer尾部
b.WriteRune(r rune) //将一个rune类型的数据放到缓冲器的尾部
b.WriteTo(w io.Writer) //将buffer中的内容输出到实现了io.Writer接口的可写入对象中

示例:
	var b bytes.Buffer
	n,_ := b.WriteString("hello")
	fmt.Printf("b: %v\n", b.Bytes())
	fmt.Printf("n: %v\n", n)

6.6 标准库errors

errors实现了操作错误的函数。语言使用error类型来返回函数执行过程中遇到的错误,如果返回的error值为nil,则表示未遇到错误,否则error会返回一个字符串,说明有问题。
 
结构:
type error interface{
    Error() string
}
1.可以用任何类型实现它,只要添加一个Error()方法接口。
2.error不一定表示一个错误,它可以表示任何信息,比如Io包中就用error类型的io.EOF表示数据读取结束,而不是遇到了什么错误
3.errors实现了最简单的error类型,只包含一个字符串,就可以记录大多数情况下遇到的错误信息。
4.基本语法:func New(text string) error

示例:
func check(s string)(string,error){
	if s == ""{
		err := errors.New("字符串不能为空")
		return "",err
	}else{
		return s, nil
	}
}

自定义错误:
import (
	"fmt"
	"time"
)
type MyError struct{
	When time.Time
	What string
}
func (e MyError) Error() string{
	return fmt.Sprintf("%v:%v",e.When,e.What)
}
func oops() error{
	return MyError{
		time.Date(1989,3,15,22,30,0,0,time.UTC),
		"the file system has go ne away",
	}
}
func main() {
	if err := oops(); err!= nil{
		fmt.Println(err)
	}
}

6.7 包Sort

sort包提供了排序切片和用户自定义数据集以及相关功能的函数
sort包主要针对[]int[]float64[]string以及其它自定义切片的排序

结构体:
type IntSlice struct
type Float64Slice
type StringSlice

函数:
func Ints(a []int)
func IntsAreSorted(a []int) bool
func SearchInts(a []int, x int) int
func Float64s(a []float64)
func Float64sAreSorted(a []float64) bool
func SearchFloat64s(a []float64,x float64) int
func SearchFloat64s(a []float64,x float64) bool
func Strings(a []string)
func StringsAreSorted(a []string)bool
func SearchStrings(a []string) bool
func Sort(data Interface)
func Stable(data Interface)
func Reverse(data Interface) Interface
func ISSorted(data Interface) bool
func Search(n int,f func(int) bool) int

示例:
	 s := []int{2,4,1,3}
	 sort.Ints(s)
	 fmt.Printf("s:%v\n",s)

接口type Interface:
type Interface interface{
    Len() int //Len方法返回集合中的元素个数
    Less(i,j int) bool //i>j,该方法返回索引i的元素是否比索引j的元素小
    Swap(i,j int) //交换i,j的值
}

示例:
    type NewInts []uint
    func (n NewInts) Len() int{
        return len(n)
    }
    func (n NewInts) Less(i,j int) bool{
        fmt.Println(i,j,n[i] < n[j],n)
        return n[i] < n[j]
    }
    func (n NewInts) Swap(i,j int){
        n[i],n[j] = n[j],n[i]
    }
    func main() {
        n := []uint{1,3,2}
        sort.Sort(NewInts(n))
        fmt.Println(n)
    }

结构体:
func (p xxxSlice) Len() int //切片长度
func (p xxxSlice) Less(i,j int) bool
func (p xxxSlice) Swap(i,j int) 
func (p xxxSlice) Search(x xxx) int
//这个和后面功能一致
func (p xxxSlice) Sort()

综合实例:
[]float64:
f := []float64{1.1,4.4,5.5,3.3,2.2}
sort.Float64s(f)
fmt.Printf("f:%v\n",f)

[]int:
f := []int{3,5,1,2,4}
sort.Ints(f)
fmt.Printf("f:%v\n",f)

[]string:
//字符串排序,先比较高位,相同再比较低位
ls := sort.StringSlice{
    "100",
    "42",
    "41",
    "3",
    "2",
}
fmt.Println(ls)
sort.Strings(ls)
fmt.Println(ls)

[][]int: // 复杂结构
type testSlice [][]int
func(l testSlice) Len() int {return len(1)}
func(l testSlice) Swap(i,j int) {l[i],l[j]=l[j],l[i]}
func(l testSlice) Less(i,j int) bool {return l[i][1]<l[j][1]}
func main(){
    ls := testSlice{
        {1,4},
        {9,3},
        {7,5},
    }
    fmt.Println(ls)
    sort.Sort(ls)
    fmt.Println(ls)
}

[]map[string]int [{"k:0"},{"k1":1},{"k2",2]}]: // 复杂结构体
type testSlice []map[string]float64
func (l testSlice) Len() int {return len(1)}
func (l testSlice) Swap(i,j int) {l[i],l[j]=l[j],l[i]}
func (l testSlice) Less(i,j int) bool {return l[i]["a"] < l[j]["a"]} //按照a对应的值排序
func main{
    ls := testSlice{
        {"a":4,"b":12}{"a":3,"b":11},
        {"a":5,"b":10},
    }
    fmt.Println(ls) //[map[a:4 b:12] map[a:3 b:11] map[a:5 b:10]]
    sort.Sort(ls)
    fmt.Println(ls) //[map[a:3 b:11] map[a:4 b:12] map[a:5 b:10]]
}

[]struct://复杂结构体
type People struct{
    Name string
    Age int
}
type testSlice []People
func(l testSlice) Len() int {return len(l)}
func(l testSlice) Swap(i,j int) {l[i],l[j]=l[j],l[i]}
func(l testSlice) Less(i,j int) {return l[i].Age < l[j].Age}
func main(){
    ls := testSlice{
        {Name:"n1",Age:12},
        {Name:"n2",Age:11},
        {Name:"n3",Age:10},
    }
    fmt.Println(ls)
    sort.Sort(ls)
    fmt.Println(ls)
}

6.8 标准库time

time包提供测量和显示时间的功能。

基本使用示例:
func test1(){
    now := time.Now() //获取当前时间
    fmt.Printf("current time:%v\n",now)
    year := now.Year()
    month := now.Year()
    day := now.Day()
    hour := now.Hour()
    minute := now.Minute()
    second := now.Second()
    fmt.Printf("%d,%02d %02d:%02d %02d\n",year,month,day,hour,minute,second)
}

时间戳:时间戳是指某个时间段至当前时间的总毫秒数,被称为Unix时间戳(例如用在web开发中cookies有效期,接口加密,Redis中Key有效期等)
示例一:Unix()
func test2(){
    now := time.Now()
    fmt.Printf("%T,%v",now.Unix(),now,Unix())
}
示例二:time.UnixNow(),time.UnixNano()
func test3(){
    now := time.Now()
    fmt.Printf("%T,%v\n",now.UnixNano(),now.UnixNano())
}

时间戳转化为普通的时间格式:time.Unix
func timeStampToTime(){
    timestamp := time.Now().Unix()
    timeObj := time.Unix(timestamp,0) //将时间戳转为时间格式
    fmt.Println(timeObj)
    year := timeObj.Year() // 年
    month := timeObj.Month() //月
    day := timeObj.Day() //日
    hour := time.Obj.Hour() //小时
    minute := timeObj.Minute() //分钟
    second := timeObj.Second() //秒
    fmt.Printf("%d-%02d-%02d %02d:%02d:%02d\n",year,month,day,hour,minute,second)
}

操作时间:ADD
func add(h,m,s,mls,msc,ns time.Duration){
    now := time.Now()
    fmt.Println(now.Add(time.Hour*h + time.Minute*m + time.Second*s + time.Millisecond*mls + time.Microsecond*msc + time.Nanosecond*ns))
}
func main(){
    test4(3,4,5,6,7,8)
}

定时器:time.Tick(时间间隔)
func tick(){
    ticker := time.Tick(time.Second) //定义一个1秒间隔的定时器
    for i := range ticker{
        fmt.Println(i) //每秒执行任务
    }
}

时间格式化:Format
func format(){
    now := time.Now()
    //24小时制
    fmt.Println(now.Format("2006-01-02 15:04:05.000 Mon Jan"))
    //12小时制
    fmt.Println(now.Foramt("2006-01-02 03:04:05.000 PM Mon Jan"))
}

解析字符串格式的时间:
now := time.Now()
fmt.Println(now)
//加载时区
loc,err := time.LoadLocation("Asia/Shanghai")
if err != nil{
    fmt.Println(err)
    return
}
//按照指定时区和指定格式解析字符串时间
timeObj,err := time.ParseInlocation("2006/01/02 15:00:00","2023/01/01 14:15:00",loc)
if err != nil{
    fmt.Println(err)
    return
}
fmt.Println(timeObj)
fmt.Println(timeObj.Sub(now))

6.9 标准库encoding/json

实现Json编码和解码,将json字符串转换为struct,或者将struct转换为json

核心函数:
func Marshal(v interface{})([]byte,error):struct编码成json,可以接受任意类型。
func Unmarshal(data []byte,v interface{}) error:将json转码为struct结构体。

核心结构体:
type Decoder struct{ //从输入流读取并解析json
    // contains filtered or unexported fields
}
type Encoder struct{ //写json到输出流
    // contains filtered or unexported fields
}

示例一:结构体转换为json
type Person struct{
    Name string
    Age int
    Email string
}
func Marshl(){
    p := Person{
        Name: "tom",
        Age: 20,
        Email: "[email protected]",
    }
    b,_ := json.Marshal(p)
    fmt.Printf("b:%v\n",string(b))
}

示例二:json转换为结构体
func Unmarshal(){
    b1 := []byte(`{"Name":"tom","Age":20,"Email":"[email protected]"`)
    var m Person
    json.Unmarshal(b1,&m)
    fmt.Printf("m:%v\n",m)
}

示例三:解析嵌套类型
func test3(){
    b := []byte(`{"Name":"tom","Age":20,"Email":"[email protected]","Parents":["tom","kite"]}`)
    var f interface{}
    json.Unmarshal(b,%f)
    fmt.Printf("f:%v\n",f)
}

示例四:解析嵌套引用类型
func test4(){
    type Person struct{
        Name string
        Age int
        Email string
        Parent []string
    }
    p := Person{
        Name: "tom",
        Age : 20,
        Email : "[email protected]",
        Parent : []string{"big tom","big kite"},
    }
    b,_ := json.Marshal(p)
    fmt.Printf("b:%v\n",string(b))
}

示例五:io流Reader Writer可以扩展到http websocket等场景
func test5(){
    //dec := json.NewDecoder(os.Stdin)
    //a.json : {"Nmae":"tom","Age":20,"Email":"[email protected]","Parents":["tom","kite"]}
    f,_ := os.Open("a.json")
    dec := json.NewDecoder(f)
    enc := json.NewEncoder(os.Stdout)
    for {
        var v map[string]interface{}
        if err := dec.Decode(&v);err != nil{
            log.Println(err)
            return
        }
        fmt.Printf("v:%v\n",v)
        if err := enc.Encode(&v); err != nil{
            log.Println(err)
        }
    }
}
}

6.10 标准库encoding/xml

xml包实现xml解析

核心函数:
func Marshal(v interface{})([]byte,error):struct编码成xml,可以接受任意类型。
func Unmarshal(data []byte,v interface{})error:将xml转码成struct结构体

核心结构体:
type Decoder struct{}:从输入流读取并解析xml
type Encoder struct{}:写xml到输出流

示例一:结构体转换为xml
type Person struct{
    XMLName xml.Nmae `xml:"person"`
    Name string `xml:"name"`
    Age int `xml:"age"`
    Email string `xml:"email"`
}
func Marshal(){
    p := Person{
        Name: "tom",
        Age: 20,
        Email: "[email protected]",
    }
    b,_ := xml.MarshalIndent(P," "," ")
    fmt.Println("b:%v\n",string(b))
}

示例二:读写文件
func read(){
    /* 
        
            tom
            20
            [email protected]
        
     */
    b,_ := ioutil.ReadFile("a.xml")
    var p Person
    xml.Unmarshal(b,&p)
    fmt.Printf("p:%v\n",p)
}
func write(){
    type Person struct{
        XMLName xml.Name `xml:"person"`
        Name string `xml:"name"`
        Age int `xml:"age"`
        Email string `xml:"email"`
    }
    p := person{
        Name:"tom",
        Age:20,
        Email:"[email protected]",
    }
    f,_ := os.OpenFile("a.xml",os.O_WRONLY,0777)
    defer f.Close()
    e := xml.NewEncoder(f)
    e.Encode(p)
}

6.11 标准库math

包含一些常量和一些有用的数学计算函数,例如:三角函数、随机数、绝对值、平方根等。

常量:
fmt.Printf("float64的最大值是:%.f\n",math.MaxFloat64)
fmt.Printf("float64的最小值是;%.f\n",math.SmallestNonzeroFloat64)
fmt.Printf("float32的最大值是:%.f\n",math.MaxFloat32)
fmt.Printf("float32的最小值是:%.f\n",math.SmallestNonzeroFloat32)
fmt.Printf("Int8的最大值是:%d\n",math.MaxInt8)
fmt.Printf("Int8的最小值是:%d\n",math.MinInt8)
fmt.Printf("Uint8的最大值是:%d\n",math.MaxUint8)
fmt.Printf("Int16的最大值是:%d\n",math.MaxInt16)
fmt.Printf("Int16的最小值是:%d\n",math.MinInt16)
fmt.Printf("Uint16的最大值是 :%d\n",math.MaxUint16)
fmt.Printf("Int32的最大值是 ;%d\n",math.MaxInt32)
fmt.Printf("Int32的最小值是 :%d\n",math.MinInt32)
fmt.Printf("Uint32的最大值是:%d\n",math.MaxUint32)
fmt.Printf("Int64的最大值是:%d\n",math .MaxInt64
fmt.Printf("Int64的最小值是:%d\n",math.MinInt64)
fmt.Printf("圆周率默认为 :%.28Bf\n",math,Pi)

你可能感兴趣的:(Golang_study,golang,学习)