Golang快速入门上手

Golang

1、介绍

简介

​ Go起源于 2007 年,并在 2009 年正式对外发布。Go 是非常年轻的一门语言,它的主要目标是“兼具 Python 等动态语言的开发速度和 C/C++ 等编译型语言的性能与安全性”。
​ Go语言是编程语言设计的又一次尝试,是对类C语言的重大改进(访问底层操作系统),它可以进行网络编程、系统编程、并发编程、分布式编程。不损失应用程序性能的情况下降低代码的复杂性,具有“部署简单、并发性好、语言设计良好、执行性能好”等优势,目前国内诸多 IT 公司均已采用Go语言开发项目
​ Go语言有时候被描述为“C 类似语言”,或者是“21 世纪的C语言”。Go 从C语言继承了相似的表达式语法、控制流结构、基础数据类型、调用参数传值、指针等很多思想,还有C语言一直所看中的编译后机器码的运行效率以及和现有操作系统的无缝适配。
​ Go语言没有类和继承的概念,所以它和 Java 或 C++ 看起来并不相同。但是它通过接口(interface)的概念来实现多态性。Go语言有一个清晰易懂的轻量级类型系统,在类型之间也没有层级之说。因此可以说Go语言是一门混合型的语言。
​ 此外,很多重要的开源项目都是使用Go语言开发的,其中包括 Docker、Go-Ethereum、Thrraform 和 Kubernetes。

教学

特点

  • 简洁、快速、安全
  • 并行、有趣、开源
  • 内存管理、数组安全、编译迅速

2、语法

基础格式

package main //包名

import "fmt" //导包

func main() { //主方法入口
    fmt.Printf("hello, world\n")
}

导包

import "fmt" //单个导包
import ( //多个导包
	"fmt"
	"math/rand"
)
//ps:调用时只需要通过 包名.方法 即可,方法首字母是大写 如math.Pi

变量类型

//基本类型
bool   //布尔型
string //字符串
int  int8  int16  int32  int64          //有符号整数型 ps:int8 占8位的整数型
uint uint8 uint16 uint32 uint64 uintptr //无符号整数
byte   //uint8 的别名
rune   //int32 的别名,代表一个Unicode码
float32 float64      //浮点类型,存在精度丢失问题
complex64 complex128 //复数 ps:complex64表示双32位浮点数(实数部分和虚数部分)

//补充
float32 //单精度浮点数,包括:1 个符号位,8 个指数位(小数位),23 个尾数位
float64 //双精度浮点数,包括:1 个符号位,11 个指数位,52 个尾数位
var f2 float32 = 3.14e2 //3.14e2=3.14*10^2,3.14e-2=3.14/10^2

//数组类型
[X]类型名 //数组

//指针(指向(存储)一个值的内存地址)
//通过指针获取内存地址存储的值  *变量 
//获取该值的内存地址  &指针变量
*int //整数型指针,可以存整数型的变量内存地址


//Map型
map //键值对类型

变量定义

// 初始值
// 数值类型为 0
// 布尔类型为 false
// 字符串为 ""(空字符串)
// 指针为 nil

//参数定义规范
var 参数名 类型
var i int //定义一个
var c, python, java bool //定义一组
var i, j int = 1, 2 //定义一组,赋初始值
k := 3 //简化定义替代var定义,自动判断类型,不能在函数体外使用

//常量定义
const World = "世界"  //定义后就不能被修改,不能使用 :=

//定义数组
var a [2]string //定义一个长度为2的空值数组,a[0],a[1]
var a [...]string{"a","b"} //赋值的数组,...表示长度为元素的数量
var a [...]string{1:"a",2:"b"} //下标赋值

//定义slice(切片)
var arr []int //定义空值切片,空切片值==nil
p := []int{2, 3, 5, 7, 11, 13} //定义长度、容量为6赋值的切片
a := make([]int, 5)   //定义长度和容量为5不赋值的切片
b := make([]int, 5,7) //定义长度为5和容量为7不赋值的切片

//指针定义
var ip *int //定义一个空的整数型指针,可以存整数型的变量内存地址 不能使用 :=

//定义自定义类型
type MyFloat float64 //给类型其别名

//定义结构体(类似于java的实体类,但是没用set、get和有参方法)
type Vertex struct {
	X, Y int 
}

//map定义
var m map[string]int = make(map[string]int)//定义空map,map[key类型]value类型
var m = make(map[string]int)//定义空map,map是空的值为nil
m1 := make(map[string]int)//定义空map
var m = map[string]int{"a":1,"b":2} //定义赋值
m := map[string]int{"a":1,"b":2} //定义赋值

//定义变量函数
hypot := func(x,y int) int {return (x+y)}

函数

//函数规范
func 函数名(变量名 类型, ...) 返回类型 { 
	代码...
  return 返回值
}

//自定义函数
func add(x int, y int) int { 
	return x + y
}

//自定义函数(简化参数格式)
func add(x , y int) int {  //简化方法也可作用到返回值
	return x + y
}

//多返回值函数
func swap(x, y string) (string, string) {
	return y, x
}
func main() {
	a, b := swap("hello", "world") //接受返回值
	fmt.Println(a, b)
}

//返回值起名
func split(sum int) (x, y int) {
	y = sum - x 
	return//起名后就会赋予初始化值,如int初始值是0,返回时不写就会直接返回值名
}

//可变参数函数
func sum(x...int) int {
		return (x[0]+x[1])
}

//匿名函数
hypot := func(x...int) int {
		return (x[0]+x[1])
}

//闭包函数
func add() func()int{
	i := 0
	return func()int {
		i+=1
		return i
	}
}

//方法接收者函数
func (v *Vertex) Abs() float64 {  //func 和 方法名 之间的参数就是方法接收者
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

接口

//定义接口
type User interface{ //创建接口
	name(s string) string //接口方法
  //... 方法
}

循环

//常规for循环
for i := 0; i < 10; i++ { /*代码块*/ }

//for条件循环
for sum < 1000 { /*代码块*/ }

//for死循环
for {}

//for迭代循环1
for i, v := range pow { /*代码块*/ }

//for迭代循环2,_忽略key
for _, value := range pow { /*代码块*/ }

判断

//if
if x < 0 {/*代码块*/}
//if..else
if v < lim {/*代码块*/} else {/*代码块*/}

//if头添加变量,变量作用域只能在if体里
func add(x int)int{
	if v := x+1;v > 10{ //定义一个if专属变量v
		return v
	}
	return x
}
func main() {
	fmt.Println(add(10))
}

//switch  不带条件就是true
func main() {
	fmt.Print("Go runs on ")
	switch os := runtime.GOOS; os {
	case "darwin":
		fmt.Println("OS X.")
	case "linux":
		fmt.Println("Linux.")
	default:
		fmt.Printf("%s.", os)
	}
}

错误/错误捕获

//抛出错误的三种方法
func (e *自定义类型) Error() string { //实现Error方法
	//代码块
}
自定义类型(x).Error() //调用

errors.New("参数为空") //通过errors.New返回,返回值类型是error

panic("出错了") //通过panic抛出错误

defer func() { //函数结束前执行匿名函数(延迟执行)
	if err:=recover();err != nil { //捕获错误
		fmt.Println("======捕获到错误=====")
		fmt.Println("异常名称:",err)
	}
}()//这个()是调用defer的匿名函数func()

3、说明

类型转换

//类型转换
var i int = 42
var f float64 = float64(i)
a := 42
b := float64(a)

切片

/*
	len长度:元素个数
	cap容量:要拷贝的数组/切片的长度-开头数
*/	    
var arr []int{0,1,2,3,4,5} //定义切片 len=6 cap=6
a:= arr[1:4]  //定义切片 len=3 cap(arr的len-开头数)=5
p[1:4] //获取值,包头不包尾,结果1 2 3
p[:3]  //获取值,不写头从0开始,包头不包尾,结果0 1 2
p[4:]  //获取值,不写尾从指定头开始获取全部,包头不包尾,结果4 5
	

/*
	添加时如果:切片cap<新元素个数+旧元素个数,会生成一个新的切片
	len=新元素个数+旧元素个数
	cap=新的len+旧元素个数
*/
arr = append(arr, 1,2,...)  //添加元素到切片

/*
  for 循环的 range 格式可以对 slice 或者 map 进行迭代循环
*/
for i, v := range pow {
		fmt.Printf("2**%d = %d\n", i, v)
}

结构体

type Vertex struct {
	X, Y int 
}
v1 = Vertex{1, 2}  //顺序赋值,按顺序赋值,结果1,2
v3 = Vertex{1}  //顺序赋值,Y被省略,结果1,0
v2 = Vertex{Y: 1}  //指定赋值,X被省略,结果0,1
v4 = &Vertex{1, 2} //类型为 *Vertex

延迟执行

//defer 等待上层函数执行完毕后才执行,等待时不会堵塞其它函数/命令
//简单点说就是,不管在那使用defer都只会在函数结束前其它函数后去执行defer的函数
func main() {
	fmt.Println("hello1") //第1输出
	defer fmt.Println("world") //第4输出
	fmt.Println("hello2") //第2输出
	time.Sleep(time.Duration(2)*time.Second)
	fmt.Println("hello3") //第3输出
}

//补充,当有多个同级defer时,会把defer进行压栈(后进先出)
func main() {
	fmt.Println("counting")
	for i := 0; i < 10; i++ {
		defer fmt.Println(i)
	}
	defer add(1)
	fmt.Println("done")
}

func add(a int){
	fmt.Println(a,"start")
}

Map

m := make(map[string]int)
m[key] = elem   //插入或修改一个元素
elem := m[key]  //获取元素value值
elem,ok := m[key] //获取元素value值 判断是否存在,不存在获取的是 elem=0 ok=false
delete(m, key)  //删除元素

闭包

//闭包特点就是常驻内存(声明后就一直在内存直到程序关闭),互不干扰
//闭包构建一个函数里返回另一个函数
func main() {
	var fn = add()
	fmt.Println(
		fn(),
		fn(),
		fn(),
	)
}

// func 函数名() 返回值[函数]{func 函数名() {返回值}}
func add() func()int{
	i := 0
	return func()int {
		i+=1
		return i
	}
}

方法

//方法接收者接受的参数可以是 自定义类型 和 结构体
//作用:可以给方法接受者(自定义类型 和 结构体)添加任意自定义方法
//首字母大写的方法外部引用时可见,反之相反
type ABC int //自定义类型
func (a ABC) Aaa() int{ //ABC是方法接收者,给接收者添加自定义方法,Aaa()方法
	return int(a)+5
}

func main() { 
	v := ABC(10) //初始化自定义方法值为10
	fmt.Println(v.Aaa()) //调用方法
}

指针

func A(f *float64){
	*f = *f + 3.14
	fmt.Println("进入A方法后f:",*f)
}

func B(f float64){
	f = f - 1
	fmt.Println("进入B方法后f:",f)
}

func main() {
	var f float64 = 3.14
	fmt.Println("初始值的f:",f)
	A(&f) //通过指针,是传入f的内存地址,可以对f进行修改
	B(f) //不通过指针,是拷贝f的副本传入B方法,修改的是副本
	fmt.Println("现在的f:",f)
}

接口

type User interface{ //创建接口
	name(s string) string //接口方法
}

type MyUser string //自定义类型
func (mu MyUser) name(s string)string{//实现User接口方法
	return s+"用户"+string(mu)
} 

func main(){
  //var u User = MyUser("bb")//实例化接口 方法1 结果:aaa用户bb
	var u User = new(MyUser)//实例化接口 方法2 结果:aaa用户
	fmt.Println(u.name("aaa"))//调用接口方法
}

并行并发

并发:任务在单线程里快速交替执行,肉眼感官上就是同时运行但其实不是
并行:多个线程同时执行多个任务

不并发、不并行:想喝开水,就一直等烧开,喝完水再去做别的事
并发:想喝开水,等水烧开过程跑去做别的事,水开了再喝
并行:想喝开水,叫别人替你烧水,自己去做别的事情

goroutine:goroutine是Go并行设计的核心。goroutine说到底其实就是线程,但是它比线程更小,十几个goroutine可能体现在底层就是五六个线程,Go语言内部帮你实现了这些goroutine之间的内存共享。执行goroutine只需极少的栈内存(大概是4~5KB),当然会根据相应的数据伸缩。goroutine比thread更易用、更高效、更轻便。
goroutine是通过Go的runtime管理的一个线程管理器。goroutine通过go关键字实现了,其实就是一个普通的函数。

//main方法坑:main方法自身不会堵塞,所以再执行go时不让main进行堵塞,main一结束程序也就结束就看不见go执行效果
//下面是个简单的励志
func main() {
	go Run("A")
	Run("b")
}

func Run(string2 string)  {
	for i := 0; i < 10; i++ {
		runtime.Gosched() //让出执行权,进入堵塞等待再次获取执行权
		fmt.Printf(string2)
	}
}

goroutine之间如何进行数据的通信呢,Go提供了一个很好的通信机制channel(通道机制)

//通过chan进行堵塞

func main() {
  c := make(chan bool) //数据通信对象,同步的(双方都执行完毕后关闭)
	//c := make(chan bool 1) //带缓冲的数据通信对象,异步的(存储方数据都存储在缓冲区即可关闭)
	go Run("A",c)
	go Run("B",c)
	go Run("C",c)
	for i := 0; i < 3; i++ {
		<- c
	}
}

func Run(s string,c chan bool)  {
	fmt.Println()
	for i := 0; i < 10; i++ {
		fmt.Printf(s)
	}
	c <- true
}
//带缓存的作用 
func main() {
	c := make(chan int, 2)//最大存储2个int
	c <- 1
	c <- 2
	fmt.Println(<-c)
	fmt.Println(<-c)
}

//显示关闭用法
func main() {
	c := make(chan int)
	go func() {
		for i := 0; i < 5; i++ {
			c <- i
		}
		close(c) //显示关闭channel
	}()
	for v := range c{ //显示关闭后结束循环
		fmt.Println(v)
	}
}
package main

import (
	"fmt"
	"runtime"
	"sync"
)

func main() {
	wg := sync.WaitGroup{} //创建等待任务
	wg.Add(3) //添加任务数量
	runtime.GOMAXPROCS(runtime.NumCPU()) //使用多线程,不设置默认是单线程
	go Run("A",&wg)
	go Run("B",&wg)
	go Run("C",&wg)
	wg.Wait() //等待任务=0,结束堵塞

}

func Run(s string,wg *sync.WaitGroup)  {
	fmt.Println()
	for i := 0; i < 10; i++ {
		fmt.Printf(s)
	}
	wg.Done() //任务数量-1
}
//多线程接受channel
//select{ case 接受/发送 case}
package main
import "fmt"

func fibonacci(c, quit chan int) {
    x, y := 1, 1
    for {
        select {
        case c <- x: //发送 c
            x, y = y, x + y
         case v,ok :=<-quit: //接受 quit,v是值 ok是chan对象是否被关闭
            fmt.Println("quit")
            return
          case <- time.After(3 * time.Second): //设置监听超时,在时间内没用接受到channel执行,不能和default一起使用
					println("timeout")
					o <- true
				break
       // default:
			/*当监听的channel都没有准备好的时候,默认执行的(select不再阻塞等待channel)
        简单来说就是:case都不满足直接运行default不进行等待
        */
        }
    }
}

func main() {
    c := make(chan int)
    quit := make(chan int)
    go func() {
        for i := 0; i < 10; i++ {
            fmt.Println(<-c)
        }
        quit <- 0
    }()
    fibonacci(c, quit)
}

4、使用

反射

//获取方法、函数、字段
package main

import (
	"fmt"
	"reflect"
)

type Word struct {
	A int
	B string
	C string
}

type dis struct {
	Word
	aut string
}

func (t Word) spelling(){
}
func (t Word) Reading(s string){
}

func main() {
	//info(Word{A: 1,B: "hello",C: "!!!"})
	//info(info) //ps:获取函数名称和获取类型名称是不一样的
	info1(dis{Word{A: 1,B: "hello",C: "!!!"},"张三"})
}

func info(i interface{}){
	t:=reflect.TypeOf(i) //获取字段、方法、函数
	v:=reflect.ValueOf(i) //调用字段、方法、函数
	fmt.Println("类型名称:",t.Name()) //获取类型名称
	//fmt.Println("函数名称:",runtime.FuncForPC(v.Pointer()).Name()) //获取函数名称
	fmt.Println("类型",t.Kind()) //获取类型 如:main.Word 类型就是struct
	fmt.Println("-----------")
	if t.Kind()!=reflect.Struct {return}
	for i:=0;i<t.NumField();i++{
		fd :=t.Field(i)
		fmt.Println("字段名称:",fd.Name,"字段类型:",fd.Type,"字段值:",v.Field(i).Interface())
	}
	fmt.Println("-----------")
	for i:=0;i<t.NumMethod();i++{ //获取不到私有方法(首字母小写方法)
		fd :=t.Method(i)
		fmt.Println("方法名称:",fd.Name,"方法类型:",fd.Type)
	}
	//t.FieldByIndex([]int{0,0})

}
func info1(i interface{}){
	t:=reflect.TypeOf(i) //获取字段、方法、函数
	v:=reflect.ValueOf(i) //获取值用
	for i:=0;i<t.NumField();i++{
		sub :=v.Field(i).Kind()
		if sub == reflect.Struct{
			for x:=0;x< reflect.TypeOf(v.Field(i).Interface()).NumField();x++{
				fmt.Println(t.FieldByIndex([]int{i,x})) //打印结构字段
			}
		}else {
			fmt.Println(t.Field(i))
		}
	}
}
package main

import (
	"fmt"
	"reflect"
)

func main() {
	/*//反射修改基本类型
	x := 100
	fmt.Println(x)
	v:=reflect.ValueOf(&x)  //获取对象指针
	v.Elem().SetInt(999) //修改值
	fmt.Println(x)*/

	//反射调用方法
	u := User{"Jke",12}
	fmt.Println(u)
	v := reflect.ValueOf(u) //获取对象
	m := v.MethodByName("Hello") //获取方法
	args := []reflect.Value{reflect.ValueOf("Make"),reflect.ValueOf(15)}//要传递的参数
	b:=m.Call(args) //执行方法
	fmt.Println(b[0])
}

type User struct {
	name string
	age int
}

func (u User) Hello(str string,i int) bool{
	fmt.Println("hello!",str,"[",i,"age]")
	return true
}

time

time.NewTimer(time.Second*1)//创建一个指定时长的定时器
time.NewTicker(time.Second*1)//创建一个指定时长的打点器
time.Tick(200 * time.Millisecond) //创建一个指定时长的打点器
ticker.Stop() //停止打点/定时器(手动停止)

原子

Add 【增加和减少 线程安全】
CompareAndSwap 【比较并交换 线程安全】
Swap 【交换 线程安全】
Load 【读取 线程不安全】
Store 【存储 线程不安全】
=============================================
Load 只保证读取的不是正在写入的值,Store只保证写入是原子操作
atomic.AddUint32(&addr,n) //Add 增
atomic.AddUint32(*addr,uint32(int32(n))) //Add 减
atomic.AddUint32(&addr,^uint32(-n-1)) //Add 减

//替换 addr等于old,addr会被替换成new
ok:=atomic.CompareAndSwapInt32(&addr,old,new)

//读取 也就是load,只能保证读取的不是正在写入的值
v:=atomic.LoadInt32(&addr)

//存储 也就是Store,只保证写入是原子操作
atomic.StoreInt32(&value,newaddr)
//load和store不保证安全例子
package main
import (
	"fmt"
	"sync"
	"sync/atomic"
)
var (
	wg  sync.WaitGroup
	mu  sync.Mutex
	num int32
)
func wrap(callback func()) {
	wg.Add(1)
	go func() {
		callback()
		wg.Done()
	}()
}

func main() {
	// 并发不安全 结果值不确定
	wrap(incNumAtomic)
	wrap(incNumAtomic)

	// 1200, 并发安全
	wrap(incNumAtomic2)
	wrap(incNumAtomic2)
	wg.Wait()
	fmt.Printf("num=%d\n", num)
}
func incNumAtomic() {
	for i := 0; i < 10000; i++ {
		// atomic.Load*系列函数只能保证读取的不是正在写入的值(比如只被修改了一半的数据)
		// 同样的atomic.Store* 只保证写入是原子操作(保证写入操作的完整性)
		val := atomic.LoadInt32(&num)
		atomic.StoreInt32(&num, val+1)
	}
}
func incNumAtomic2() {
	for i := 0; i < 600; i++ {
		atomic.AddInt32(&num, 1)
	}
}

互斥锁

var mutex = &sync.Mutex{} //创建对象
mutex.Lock()   //上锁
mutex.Unlock() //解锁
//&sync.Mutex{} 创建互斥锁,mutex.Lock()上锁,mutex.Unlock()解锁
package main
import (
	"fmt"
	"sync/atomic"
	"time"
)
func main() {
	var mutex = &sync.Mutex{}
	sum := 0
  sum1 := 0
	for i := 0; i < 1000; i++ {
		go func() {
			mutex.Lock()
			sum1 += 1
			mutex.Unlock()
		}()
	}
	for i := 0; i < 1000; i++ {
		go func() {
			sum += 1
		}()
	}
	time.Sleep(time.Second*3)
	fmt.Println(sum,"===",sum1)
}

排序

sort.Strings(x)  //对string切片进行升序排序
sort.Ints(x) //对int切片进行升序排序
sort.IntsAreSorted(ints) //判断int切片是否进行了升序排序,其它同类就是改类型
package main
import (
	"fmt"
	"sort"
)
type byLength []string

//实现sort.Interface的方法 len、Swap、Less就可以进行自定义的排序
func (s byLength) Len() int {//为集合内元素的总数
	return len(s)
}
func (s byLength) Swap(i, j int) {//替换方式
	s[i], s[j] = s[j], s[i]
}
func (s byLength) Less(i, j int) bool { //比较方式
	return len(s[i]) < len(s[j])
}

func main() {
	fruits := []string{"peach", "banana", "kiwis"}
	sort.Sort(byLength(fruits))
	fmt.Println(fruits)
}

strings

strings.HasPrefix(s, prefix)//判断字符串s是否以prefix开头 
strings.HasSuffix(s, suffix)//判断字符串s是否以suffix结尾

strings.Index(s, str)//判断str在s中首次出现的位置,没有返回-1
strings.LastIndex(s , str)//判断str在s中最后出现的位置,没有返回-1

strings.Replace(str, old , new, n)//字符串替换, n为替换次数

strings.Count(str , substr)//统计substr出现个数
strings.Repeat(str , count)//重复count次str 如a重复5次=aaaaa

strings.ToLower(str)//转为小写
strings.ToUpper(str)//转为大写

strings.TrimSpace(str)//去掉字符串首尾空白字符
strings.Trim(str, cut)//去掉字符串首尾cut字符
strings.TrimLeft(str, cut)//去掉字符串包含cut的字符
strings.TrimRight(str, cut)//去掉字符串包含cut的字符
strings.TrimSuffix(str, cut)//删除字符串里的cut字符

strings.Field(str)//返回空格分隔的所有子串
strings.Split(str, split)//返回split分隔的所有子串

strings.Join([]s1, sep)//用sep拼接切面

strings.Itoa(i)//把一个整数i转成字符串
strings.Atoi(str)//把一个字符串转成整数

字符串格式化

fmt.Printf("%v\n", p)
/*
	%v  输出值
	%+v 输出括结构体时包含字段名
	%#v 该值的源码片段
	%T  类型
	%t  布尔型
	%d  十进制格式输出
	%b  二进制格式输出
	%c  输出给定整数的对应字符
	%x  输出十六进制编码
	%f  浮点型输出
	%e  科学记数法浮点型输出
	%E  同上
	%s  基本的字符串输出
	%q  带双引号的输出
	%x  使用 base-16 编码的字符串,每个字节使用 2 个字符表示
	%p  输出指针
	%xd x是数字,表示占x个位符,右对齐
	%-6d x是数字,表示占x个位符,左对齐
*/

正则表达式

//查找出匹配正则的语句 或 规定满足正则的语句 pattern:正则语句 str:要匹配的字符串
regexp.MatchStrin(pattern,str) //Match类型 判断字符串是否满足正则语句 返回bool

regexp.Compile(pattern)  //创建正则表达式
regexp.MustCompile(pattern) //创建正则表达式 适用于常量

regexp.FindString(str) //查找匹配的字符串
regexp.ReplaceAllFunc(str, ide) //用ide替换满足条件的文本

JSON

// JSON 的编码和解码 编码:数据转JSON  解码:JSON转数据
// 编码
func main() {
	type Stu struct {
    Name  string `json:"name"` //编码时指定key名
    Age   int
    HIgh  bool
	}
  stu := Stu{
    Name: "张三",
    Age:  18,
    HIgh: true,}
  jsonStu, err := json.Marshal(stu) //编码
  if err != nil {fmt.Println("生成json字符串错误")} //判断是否编码成功
}


// 解码
func main() {
  type StuRead struct {
    Name  interface{} 
    Age   interface{}
    HIgh  interface{}
	}
  
 //json字符中的"引号,需用\进行转义,否则编译出错
 //json字符串沿用上面的结果,但对key进行了大小的修改,并添加了sex数据
 data:="{\"name\":\"张三\",\"Age\":18,\"high\":true,\"sex\":\"男\",\"CLASS\":{\"naME\":\"1班\",\"GradE\":3}}"
  str:=[]byte(data) //将字符串转byte(字符转字节码)
  
  stu:=StuRead{}
  err:=json.Unmarshal(str,&stu) //解码,第二个参数必须是指针
  
  //解析失败会报错,如json字符串格式不对,缺"号,缺}等。
    if err!=nil{
        fmt.Println(err)
    }
}

//对html特殊字符进行转义
buffer := &bytes.Buffer{} //带缓存的byte
encoder := json.NewEncoder(buffer) //创建新的编码
encoder.Encode(t)//编码

XML

type Plant struct {
	XMLName xml.Name `xml:"config"` //根标签
	Id      int      `xml:"id,attr"` //根标签属性
	Age		int		`xml:"age,attr"` //根标签属性
	Name    string   `xml:"plant2"`	 //标签
	Origin  []string `xml:"chi>origin"`	 //标签
}
xml.MarshalIndent(a, " ", "  ") //生成成xml
xml.Unmarshal(data, &v) //解码成对象
package main

import (
	"encoding/xml"
	"fmt"
)

type Plant struct {
	XMLName xml.Name `xml:"config"` //根标签
	Id      int      `xml:"id,attr"` //根标签属性
	Age		int		`xml:"age,attr"` //根标签属性
	Name    string   `xml:"plant2"`	 //标签
	Origin  []string `xml:"chi>origin"`	 //标签
}


type Result struct {
	XMLName xml.Name `xml:"persons"`
	Persons []*Plant `xml:"person"`
}

func (p Plant) String() string {
	return fmt.Sprintf("Plant id=%v, name=%v, origin=%v",
		p.Id, p.Name, p.Origin)
}

func main() {
	coffee := &Plant{Id: 27, Name: "张三"}
	coffee.Origin = []string{"Ethiopia", "Brazil"}
	coffee2 := &Plant{Id: 27, Name: "历史"}
	coffee2.Origin = []string{"Ethiopia", "Brazil"}

	a := &Result{}
	a.Persons = []*Plant{coffee,coffee2}

	out, _ := xml.MarshalIndent(a, " ", "  ") //生成成xml
	fmt.Println(xml.Header +string(out))
}

//xml.Unmarshal(data, &v) //解码成对象

time

p := fmt.Println 
then := time.Now() //当前时间戳
p(then.Year())    //获取当前年
p(then.Month())   //获取当前月
p(then.Day())     //获取当前日
p(then.Hour())    //获取当前小时
p(then.Minute())  //获取当前分
p(then.Second())  //获取当前秒
p(then.Nanosecond() / 1000)//获取当前毫秒
p(then.Nanosecond())//获取当前纳秒
p(then.Location())//获取所在时区

time.Unix(now.Unix(), 0)//根据时间戳返回本地时间,秒数和纳秒数

p(then.Date())      //获取当前日期
p(then.Weekday())   //获取星期
p(then.Before(now)) //then时间在now时间之前,返回真
p(then.After(now))  //then时间在now时间之后,返回真
p(then.Equal(now))  //then等于now,返回真

p(then.Add(diff))   //时间计算

t.Format("2006-01-02T15:04:05.999999-07:00") //格式化时间

随机数

rand.Seed(time.Now().UnixNano()) //生成随机数
fmt.Println(rand.Intn(10)) //打印随机数

数字转换

f, _ := strconv.ParseFloat("1.234", 64) //字符串转浮点型,其它类型同理把类型修改即可
i, _ := strconv.ParseInt("123", 0, 64) //字符串转整数型
k, _ := strconv.Atoi("135") //字符串转基础的 10 进制,失败返回错误信息

url

type URL struct {
    Scheme   string
    Opaque   string    // 编码后的不透明数据
    User     *Userinfo // 用户名和密码信息
    Host     string    // host或host:port
    Path     string    // 路径
    RawQuery string    // 编码后的查询字符串,没有'?'
    Fragment string    // 引用的片段(文档位置),没有'#'
}

url.QueryEscape("/user?id=12") //将字符地址转为安全地址

func main() {
	compelePath := "http://lcoalhost:8080/user/ss?id=1&id2=1&id3=1"

	f := fmt.Println
	sP, _ := url.Parse(compelePath) //解析字符串url
	cp,_ :=url.ParseRequestURI(compelePath)//解析字符串url,但不包含[#fragment]
	ap,_ :=url.ParseQuery(compelePath) //解析字符串url,字符串变成map类型
	f(cp)
	f(sP)
	f(ap)
	f(sP.Host) //返回域名
	f(sP.Path) //返回子路径 如:域名/user/ss  子路径:/user/ss
	f(sP.RequestURI()) //返回子路径+参数
	f(sP.RawQuery) //返回url的参数,如:id=2&id2=3 返回字符串
	f(sP.Query())  //返回url的参数,返回类型是Map
	//f(sP.Query().Add) //添加参数 Del删除 Get获取参数
	f(sP.IsAbs())  //是否为绝对地址
	args := url.Values{
		"name": []string{"coco"},
		"age":  []string{"34"},
	}
	f(args.Encode()) //把结构体转url参数

	compelePath2, _ :=url.Parse("/user/ss?id=1&id2=1&id3=1")
	fmt.Println(sP.ResolveReference(compelePath2))//通过URI将一个URI补全为对URI
}

os、io、buf、file

filepath.Join("dir1", "dir2", "filename","Q.TXT")//拼接字符串为目录路径
filepath.Dir(p)  //获取文件父级路径
filepath.Base(p) //获取当前选中的文件或者文件夹
filepath.IsAbs("dir1/dir2") //是否是绝对路径
filepath.Split(p) //路径分解为父级路径和当前文件或目录
filepath.Ext(filename) //获取文件后缀
filepath.Rel("a/b/file", "a/b/t/file") //两者之间的相对路径
filepath.Walk(path,func(){}) //遍历路径下全部文件和文件夹,并每次都调用func(){}

//————————————————————————————————————————————————————————————————————————————
ioutil.WriteFile("./a.txt",[]byte("AAA"),0666) //字符串写入文件
ioutil.ReadDir("pkg") //获取目录下全部文件
ioutil.ReadAll(os.Open("./a.txt")) //读取文件内容
ioutil.TempDir("DIR", "tmp") //在DIR目录下创建名为tmp的临时目录

//————————————————————————————————————————————————————————————————————————————
bufio.NewScanner(os.Stdin) //命令行控制台输入
bufio.NewScanner(os.Stdin).scanner.Scan() //获取控制台输入流状态

bufio.NewWriter(os.Create("./c.txt")) //创建新的 Writer 对象
bufio.NewWriter(os.Create("./c.txt")).WriteString("bufferedn") //写入字符串到文件

bufio.NewReader(os.Create("./b.txt")) //文件读取到Reader中
bufio.NewReader(os.Create("./c.txt")).Seek(offset, whence) //更改选中光标【参数1:偏移量】【参数2:0从头,1当前,2末尾】

//————————————————————————————————————————————————————————————————————————————
os.Remove(path) //删除当前目录下的文件
os.RemoveAll("a") //删除整个目录

os.Mkdir("a", 0666) //创建目录
os.Create("./b.txt") //创建文件

os.Stat("./a.txt") //获取文件信息
os.Getwd() //获取当前路径
os.Open("./a.txt")//只能用作读取的文件
os.OpenFile("./a.txt", os.O_APPEND, 0666) //以追加模式打开文件

os.Setenv("FOO", "1")//设置环境变量
os.Getenv("FOO")//获取环境变量的值
os.Environ()//列出所有环境变量键值对,可以使用strings.SplitN(e, "=", 2)分割获取

命令

//命令行参数
os.Args //获取运行时传入的参数  如:go run dome.go 12 15 [12 15]都是参数下标1开始

//命令行标志 go run dome.go -word=opt  [-word]就是标志
flag.String("word", "foo", "a string") //设置标志名为word 默认值foo 描述为a string
flag.Int("numb", 42, "an int")
flag.Bool("fork", false, "a bool")
var svar string
flag.StringVar(&svar, "svar", "bar", "a string var")//指针参数需要在第一个参数传入

//子命令标志
barCmd := flag.NewFlagSet("bar", flag.ExitOnError) //设置最高级命令
barLevel := barCmd.Int("level", 0, "level") //给最高命令添加子命令

//退出命令
os.Exit(2)//使用os.Exit时defer将不会被执行,用来正常退出程序,0为正常退出码,非零表示错误码(其实没区别)

//针对linux系统
//调用命令台命令
dateCmd := exec.Command("java","--help")//调用cmd方法
dateCmd.Output()//返回结果

exec.LookPath("ls")//在环境变量中查找可执行二进制文件

sigs := make(chan os.Signal, 1) //创建通道
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) //signal接受ctrl+c结束的信号

http

//模拟Get请求
http.Get("http://gobyexample.com") //Get请求
bufio.NewScanner(http.Get("http://gobyexample.com").Body)//把Get请求结果给命令行控制台
scanner.Text() //打印结果

//Handle
http.Handle("/hello",Hello{})     //添加对应的处理ServeHTTP,对应web案例
http.HandleFunc("/hello", hello)  //访问指定路径时调用指定方法
http.ListenAndServe(":8090", nil) //绑定端口监听器

5、坑

go语言坑

main

//main方法本身是不堵塞的,在使用goroutine(并发并行)时,不对main进行堵塞时看不见goroutine结果

切片

//切片容器满时添加新元素,底层会重新分配地址,因此原本的切片也就不是同一个
func main(){
 a:make([]int,0) //原本的切片容器大小0
  add(a) 
  fmt.Println(a) //想象中是结果[12] 实际是[] 因为内存地址不一样了
}

func add(s []int){
  append(s,12) //容器不够,重新分配了地址并添加旧元素和新元素进去
  //s :=  append(s,12) //所以建议变量存储并返回
}

大小写

//首字母大写方法可以被外部访问,小写就不行
//首字母大写方法可以被反射获取,小写不行(还没找到解决方法)

6、案例

速率限制

//控制资源的使用速率(规定多久重复执行一次)
package main

import (
	"fmt"
	"time"
)

func main() {
	//第一部分 重复速率限制
	requests := make(chan int, 5)
	for i := 1; i <= 5; i++ {
		requests <- i //赋值5次
	}
	close(requests)
	limiter := time.Tick(200 * time.Millisecond) //200毫秒的打点器(速率)
	for req := range requests { //输出
		<-limiter //启动打点器
		fmt.Println("request", req, time.Now()) //打印结果
	}

	//第二部分 运行变数速率限制
	burstyLimiter := make(chan time.Time, 3) //核心channel 存储3个定时器
	for i := 0; i < 3; i++ {
		burstyLimiter <- time.Now() //存储3个没用限制定时器
	}
	go func() {
		for t := range time.Tick(200 * time.Millisecond) {//存储200毫秒的打点器(速率)
			burstyLimiter <- t //因为核心chan存满了,所以会进入堵塞
		}
	}()

	burstyRequests := make(chan int, 5)//关键channel,最大存储5次
	for i := 1; i <= 5; i++ {
		burstyRequests <- i//赋值5次
	}
	close(burstyRequests)
	for req := range burstyRequests {//受关键channel影响,只能读5次,所以只会对堵塞的burstyLimiter赋值2次
		<-burstyLimiter //取出核心channel,解除堵塞
		fmt.Println("request", req, time.Now())
	}
}

加密

//SHA1(散列)加密

func main() {
	//SHA1 散列(hash)创建新的散列,可以应用于加密(需要三步骤 sha1.New(),h.Write,h.Sum)
	s := "sha1 this string"
	s2 := "this this this"
	h := sha1.New() //md5加密类似md5.New()
	h.Write([]byte(s))//处理字符串为切片
	bs := h.Sum(nil)//得到最终的散列值的字符切片,可以追加额外的字节切片,一般不会用

	h.Reset()//每次加密一个字符串需要Reset,生成一个新的散列
	h.Write([]byte(s2))
	fmt.Printf("%x\n",h.Sum(nil))
	fmt.Printf("%x\n", bs)
}
//base64 编解码
func main() {
	data := "abc123!?$*&()'-=@~"
	sEnc := b64.StdEncoding.EncodeToString([]byte(data)) //编码 string转base64
	fmt.Println(sEnc)
	sDec, _ := b64.StdEncoding.DecodeString(sEnc) //解码 base64转string
	fmt.Println(string(sDec))
	fmt.Println()
	uEnc := b64.URLEncoding.EncodeToString([]byte(data))
	fmt.Println(uEnc)
	uDec, _ := b64.URLEncoding.DecodeString(uEnc)
	fmt.Println(string(uDec))
}

读写文件

//读文件
package main

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"os"
)

func main() {
	read2()
}

func check(e error) {
	if e != nil && e != io.EOF{
		panic(e)
	}
}

//方法一
func read(){
	f, err := os.Open("./a.txt") //打开一个文件
	check(err)
	defer f.Close() //处理结束后关闭文件
	r := bufio.NewReader(f) //文件读取到Reader中
	buf :=make([]byte,8)//每次读取多少
	var sum []byte//用来存储每次读取的byte
	for  {
    //r.Seek(offset, whence) //更改选中光标【参数1:偏移量】【参数2:0从头,1当前,2末尾】
		n, err := r.Read(buf)
		check(err)
		if n == 0{
			break
		}
		//buf... == buf[0],buf[1]...
		sum = append(sum, buf...)
	}
	fmt.Println(string(sum))
}

//方法二
func read1(){
	//一次性读取适合读取内容比较小的文件
	//一次性读取文件
	f, err := ioutil.ReadFile("./a.txt") //一次性读取文件,返回是byte[]
	check(err)
	fmt.Println(string(f))
}

//方法三
func read2() {
  //读取到file中,再利用ioutil将file直接读取到[]byte中, 这是最优
	f, err := os.Open("./a.txt")
	check(err)
	defer f.Close()
	fd, err := ioutil.ReadAll(f)
	check(err)
	fmt.Println(string(fd))
}
//写文件
package main

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"os"
)

func checks(e error) {
	if e != nil && e != io.EOF{
		panic(e)
	}
}

func main() {
	write3()
}

//方法一
func write(){
	d1 := []byte("eeeeeeee")
	err := ioutil.WriteFile("./a.txt",d1,0777)
	checks(err)
	reads()
}

//方法二
func write1(){
	f, err := os.OpenFile("./a.txt", os.O_APPEND, 0666) //以追加模式打开文件
	//os.Create(filename) //文件不存在,可以使用这个
	checks(err)
	defer f.Close()
	_, err = io.WriteString(f, "\nccccccccc")
	checks(err)
	reads()
}

//方法三
func write2(){
	f, err := os.Create("./b.txt") //创建文件
	checks(err)
	defer f.Close()
	_, err = f.Write([]byte("AAAAA")) //写入文件(字节数组)
	checks(err)
}

//方法四
func write3(){
	f, _:= os.Create("./c.txt")//创建文件
	defer f.Close()
	w := bufio.NewWriter(f) //创建新的 Writer 对象
	w.WriteString("bufferedn") //写入字符串
	w.Flush()
	f.Close()
}

func reads() {
	f, err := os.Open("./a.txt")
	checks(err)
	defer f.Close()
	fd, err := ioutil.ReadAll(f)
	checks(err)
	fmt.Println(string(fd))
}

键盘输入

func main() {
	scanner := bufio.NewScanner(os.Stdin) //获取键盘输入流
	for scanner.Scan() { //堵塞
		ucl := strings.ToUpper(scanner.Text())//获取键盘输入的文字
		fmt.Println(ucl)
	}
	if err := scanner.Err(); err != nil {
		fmt.Fprintln(os.Stderr, "error:", err)
		os.Exit(1)
	}
}

文件目录

//跨平台文件路径(根据操作系统构建目录路径)
//如Linux 下的 dir/file 和 Windows 下的 dir\file 。
func main() {
	p := filepath.Join("dir1", "dir2", "filename","Q.TXT")//拼接为路径
	fmt.Println("p:", p)//结果:p: dir1\dir2\filename

	fmt.Println(filepath.Join("dir1//", "filename"))//Join会自动去除删除多余的分隔符和目录
	fmt.Println(filepath.Join("dir1/../dir1", "filename"))
	fmt.Println("Dir(p):", filepath.Dir(p))//获取文件父级路径
	fmt.Println("Base(p):", filepath.Base(p))//获取当前选中的文件或者文件夹

	fmt.Println(filepath.IsAbs("dir1/dir2"))//是否是绝对路径
	fmt.Println(filepath.Split(p)) //路径分解为父级路径和当前文件或目录
	filename := "config.json"
	ext := filepath.Ext(filename)//获取文件后缀
	fmt.Println(ext)
	fmt.Println(strings.TrimSuffix(filename, ext))//删除字符串里的ext字符

	rel, err := filepath.Rel("a/b/file", "a/b/t/file")//两者之间的相对路径
	if err != nil { 
		panic(err)
	}
	fmt.Println(rel)
	rel, err = filepath.Rel("a/b", "a/c/t/file")
	if err != nil {
		panic(err)
	}
	fmt.Println(rel)
}
package main

import (
	"fmt"
	"io/ioutil"
	"os"
)

func main() {
	readdir()
}

//删除文件
func rm(){
	path := "a.txt"
	err := os.Remove(path) //删除当前目录下的文件
	if err!=nil {
		fmt.Println("file not found ")
	}
}

//删除文件夹
func rmdir(){
	err := os.Mkdir("a", 0666) //创建文件
	if err!=nil {
		fmt.Println("file create fail")
	}
	defer os.RemoveAll("a") //删除整个目录
}

//列出文件夹下全部文件
func readdir(){
	getwd, _ := os.Getwd()
	fmt.Println("当前路径",getwd) //获取当前路径
	dir, err := ioutil.ReadDir("pkg") //获取目录下全部文件
	if err!=nil {
		fmt.Println("not fileDir")
	}
	for _,i := range dir {
		fmt.Println(i.Name(),"----",i.IsDir())
	}
}

timer(定时器)

//定时器
package main
import (
	"fmt"
	"time"
)

func main() {
	timer := time.NewTimer(time.Second*1)//创建一个指定时长的定时器
	fmt.Println("倒计时:",time.Now())

	go func() {
		t := <- timer.C //定时器到点执行
		fmt.Println("时间到:",t)
	}()

	timer.Stop() //停止定时器(手动停止)

	time.Sleep(2 * time.Second)
}

ticker(打点器)

//打点器 (循环的定时器,每到指定时长就运行一次)
package main

import (
	"fmt"
	"time"
)

func main() {
	ticker := time.NewTicker(time.Second*1)//创建一个指定时长的打点器
	fmt.Println("开始时间:",time.Now())
	done := make(chan bool)
	go func() {
		for {
			select { 
			case <-done:
				return
			case t := <-ticker.C://获取打点器并到点执行
				fmt.Println("打卡:",t)
			}
		}
	}()

	time.Sleep(5*time.Second)
	ticker.Stop() //停止打点器(手动停止)
	done <- true
	fmt.Println("结束")
}

web

package main

import (
	"fmt"
	"log"
	"net/http"
)

type Hello struct{}
type String string
func(s String) ServeHTTP(w http.ResponseWriter, r *http.Request){
	fmt.Fprintln(w, "Method:",r.Method) //获取请求方法
	//r.PostFormValue["text"]获取多个参数(如:复选框)
	fmt.Fprintln(w, "Post_Param:",r.PostFormValue("text"))//获取单个Post参数
	fmt.Fprintln(w, "Get_Param:",r.URL.Query()) //获取Get参数列表,获取单个get参数query["key"][0]
	fmt.Fprintln(w, "header:",r.Header)//获取请求头
	fmt.Fprintln(w, "RemoteAddr:",r.RemoteAddr)//获取远程主机
	fmt.Fprintln(w, "url:",r.URL)//获取请求地址
}

func (h Hello) ServeHTTP(w http.ResponseWriter,r *http.Request) {
	fmt.Fprintln(w, "method:",r.Method)
}

func main() {
	http.Handle("/string",String("abc")) //添加对应的处理ServeHTTP
	http.Handle("/hello",Hello{})
	//http.HandlerFunc() //添加对应的处理函数
	err := http.ListenAndServe("localhost:4000", nil)//web监听器
	if err != nil {
		log.Fatal(err)
	}
}

接受退出信号

func main() {

	sigs := make(chan os.Signal, 1)
	done := make(chan bool, 1)

	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)//signal.Notify 注册给定的通道,用于接收特定信号

	go func() {
		sig := <-sigs //接受退出信息
		fmt.Println()
		fmt.Println(sig)
		done <- true //结束堵塞
	}()

	fmt.Println("awaiting signal")
	<-done //解除堵塞
	fmt.Println("exiting")
}

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