GO语言上手|青训营

98 阅读6分钟

特点及应用场景

  1. 高性能,高并发 同c++,java的高性能
    不用第三方库来开发应用,标准库就可以开发高并发应用程序
  2. 语法简单 类似C语法
    上手容易,一周就行
  3. 丰富的标准库 很高的稳定性
  4. 工具链完善 编译,等
    性能测试
  5. 静态编译
  6. 快速编译
  7. 跨平台 各种操作系统,安卓也行,路由器

入门

语法

main包 fmt:格式化输入输出,字符串等 go run :运行

变量

go 是强类型语言 字符串:可直接加号拼接,= 比较 整型,浮点型

变量的声明

  1. var 变量名 = xxx 会自动根据xxx判断变量类型
  2. 显示地声明:var 变量名 int = 1
  3. 变量名 := float32(e)

常量

var改成const即可 根据使用的上下文自动确定类型

if else

if 条件 条件两边不用括号

for循环

只有这一种循环,没有while
就变量声明有点不一样

switch分支结构

switch a{
    case 1:
    case 2:
    ...
    ...
    default:
}

不用break,默认对应上一种情况之后,执行完毕,就会退出循环
变量a可以是任意类型,字符串,结构体 switch 不接变量,也是一种写法

数组

var a [5] int 固定长度

切片

长度可变

创建切片:make

s:= make([]string,3)

追加元素:append

s = append(s,"d","e") 注意:要把append的结果赋值给原数组 slice的原理:存储了一个长度(容量)和一个指向数组的指针,在append操作的时候,若容量不够,会扩容并返回新的slice ###切片操作(同python)

s[2:5]
s[:5]
s[2:]

不包括5
不支持负数索引

map

创建空map

m := make(map[string] int) 
//key的类型:string
//value的类型:int
m["one"] = 1  
m["two"] = 2
fmt.Println(m)//map[one:1 two:2]
fmt.Println(len(m)) // 2

删除键值对:delete

delete(m,"one")

map是完全无序的

m2 := map[string]int{"one":1,"two":2}
var m3 := map[string]int{"one":1,"two":2}

某个没有value的key

fmt.Println(m["unknow"]) // 0
r,ok := m["unknow"]      // r = 0, ok = false
//加了ok,来知晓这个key的value存不存在

range 快速遍历

若是数组,第一个是索引,第二个是值

nums := []int{2, 3, 4}
for i, num := range nums {
     fmt.Println(i,num)
}
//遍历 index,num:0,2  1,3  2,4

若只有一个值,如:

for num := range nums {
     fmt.Println(num)
}
//就是第一个值,这里是index
// 0 1 2

若只想要第二个值,就要用下划线
for _, num := range nums {
     fmt.Println(i,num)
}
//2 3 4

若是map,则第一个是key,第二个是value

类似的,若只用一个变量,则是key的值 若只要value,则用下划线

函数

golang:变量类型是后置的 函数原生支持返回多个值 在实际的业务逻辑代码里面几乎所有的函数都返回两个值,第一个是真正的返回结果,第二个值是一个错误信息。类似v和ok

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

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

func exists(m map[string]string, k string) (v string, ok bool) {
	v, ok = m[k]
	return v, ok
}

func main() {
	res := add(1, 2)
	fmt.Println(res) // 3

	v, ok := exists(map[string]string{"a": "A"}, "a")
	fmt.Println(v, ok) // A True
}

指针

主要用途:对传入参数进行修改
查看是否用指针的区别:

func add2(n int) {
	n += 2
}

func add2ptr(n *int) {
	*n += 2
}

func main() {
	n := 5
	add2(n)
	fmt.Println(n) // 5
	add2ptr(&n)
	fmt.Println(n) // 7
}

函数定义的时候是*
调用的时候用&
在计算的时候同样有解引用*

结构体

定义

type user struct{
	name 	 string
	password stirng
}

初始化

a := user{name:"wang", password:"1024"}
b := user{"wang", "1024"}
c := user{name: "wang"}
c.password = "1024"
var d user
d.name = "wang"
d.password  = "1024"

示例

fmt.Println(a, b, c, d) // {wang 1024} {wang 1024} {wang 1024} {wang 1024}
fmt.Println(checkPassword(a, "haha"))   // false
fmt.Println(checkPassword2(&a, "haha")) // false

func checkPassword(u user, password string) bool {
	return u.password == password
}
//留意这里的指针使用
func checkPassword2(u *user, password string) bool {
	return u.password == password
}

结构体方法

类似于类的成员函数 示例:

//对比上面的普通函数,user的位置变了,从传入参数列表中移到了前面
func (u user) checkPassword(password string) bool {
	return u.password == password
}

func (u *user) resetPassword(password string) {
	u.password = password
}

func main() {
	a := user{name: "wang", password: "1024"}
	a.resetPassword("2048")
	fmt.Println(a.checkPassword("2048")) // true
}

错误处理

在返回值中加入一个error,代表这个函数可能会返回错误 error可通过errors.New("message")来创建 可以用if else 来处理错误 示例如下:

func findUser(users []user, name string) (v *user, err error) {
	for _, u := range users {
		if u.name == name {
			return &u, nil
		}
	}
	return nil, errors.New("not found")
}

func main() {
	u, err := findUser([]user{{"wang", "1024"}}, "wang")
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(u.name) // wang

	if u, err := findUser([]user{{"wang", "1024"}}, "li"); err != nil {
		fmt.Println(err) // not found
		return
	} else {
		fmt.Println(u.name)
	}
}

字符串操作

func main() {
	a := "hello"
//是否包含
	fmt.Println(strings.Contains(a, "ll"))                // true
//技术
	fmt.Println(strings.Count(a, "l"))                    // 2
	fmt.Println(strings.HasPrefix(a, "he"))               // true
	fmt.Println(strings.HasSuffix(a, "llo"))              // true

	fmt.Println(strings.Index(a, "ll"))                   // 2
	fmt.Println(strings.Join([]string{"he", "llo"}, "-")) // he-llo
	fmt.Println(strings.Repeat(a, 2))                     // hellohello
	fmt.Println(strings.Replace(a, "e", "E", -1))         // hEllo
	fmt.Println(strings.Split("a-b-c", "-"))              // [a b c]
	fmt.Println(strings.ToLower(a))                       // hello
	fmt.Println(strings.ToUpper(a))                       // HELLO
	fmt.Println(len(a))                                   // 5
	b := "你好"
//中文一个字是多个字符
	fmt.Println(len(b)) // 6
}

字符串格式化

import "fmt"

type point struct {
	x, y int
}

func main() {
	s := "hello"
	n := 123
	p := point{1, 2}
//Println 打印多个变量并且换行
	fmt.Println(s, n) // hello 123
	fmt.Println(p)    // {1 2}
//Printf:
//%v 打印任意类型的变量
//%+v 打印详细结果
//%#v 打印更详细结果
	fmt.Printf("s=%v\n", s)  // s=hello
	fmt.Printf("n=%v\n", n)  // n=123
	fmt.Printf("p=%v\n", p)  // p={1 2}
	fmt.Printf("p=%+v\n", p) // p={x:1 y:2}
	fmt.Printf("p=%#v\n", p) // p=main.point{x:1, y:2}
//保留两位小数的浮点数:
	f := 3.141592653
	fmt.Println(f)          // 3.141592653
	fmt.Printf("%.2f\n", f) // 3.14
}

JSON处理

import (
	"encoding/json"
	"fmt"
)
//结构体每个字段的第一个字母是大写,在go里面就是公开字段,那么这个结构体就可以用json序列化
type userInfo struct {
	Name  string
//若需要输出小写,可以加一个json tag 修改,如下,Age在json序列化输出后是age
	Age   int `json:"age"`
	Hobby []string
}

func main() {
	a := userInfo{Name: "wang", Age: 18, Hobby: []string{"Golang", "TypeScript"}}
//序列化
	buf, err := json.Marshal(a)
	if err != nil {
		panic(err)
	}
//序列化后得到数组buf
	fmt.Println(buf)         // [123 34 78 97...]
	fmt.Println(string(buf)) // {"Name":"wang","age":18,"Hobby":["Golang","TypeScript"]}

	buf, err = json.MarshalIndent(a, "", "\t")
	if err != nil {
		panic(err)
	}
	fmt.Println(string(buf))

	var b userInfo
//反序列化到一个空的变量b里面
	err = json.Unmarshal(buf, &b)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%#v\n", b) // main.userInfo{Name:"wang", Age:18, Hobby:[]string{"Golang", "TypeScript"}}
}

时间处理

import (
	"fmt"
	"time"
)

func main() {
//当前时间
	now := time.Now()
	fmt.Println(now) // 2022-03-27 18:04:59.433297 +0800 CST m=+0.000087933
//时区
	t := time.Date(2022, 3, 27, 1, 25, 36, 0, time.UTC)
	t2 := time.Date(2022, 3, 27, 2, 30, 36, 0, time.UTC)
	fmt.Println(t)                                                  // 2022-03-27 01:25:36 +0000 UTC
	fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute()) // 2022 March 27 1 25
	fmt.Println(t.Format("2006-01-02 15:04:05"))                    // 2022-03-27 01:25:36
//时间段
	diff := t2.Sub(t)
	fmt.Println(diff)                           // 1h5m0s
	fmt.Println(diff.Minutes(), diff.Seconds()) // 65 3900
	t3, err := time.Parse("2006-01-02 15:04:05", "2022-03-27 01:25:36")
	if err != nil {
		panic(err)
	}
	fmt.Println(t3 == t)    // true
//时间戳
	fmt.Println(now.Unix()) // 1648738080
}

数字解析

字符串和数字之间的转换


import (
	"fmt"
	"strconv"
)

func main() {
	f, _ := strconv.ParseFloat("1.234", 64)
	fmt.Println(f) // 1.234

//字符串,进制,精度
	n, _ := strconv.ParseInt("111", 10, 64)
	fmt.Println(n) // 111

	n, _ = strconv.ParseInt("0x1000", 0, 64)
	fmt.Println(n) // 4096

//快速把字符串转成十进制数字
	n2, _ := strconv.Atoi("123")
	fmt.Println(n2) // 123

//不合法
	n2, err := strconv.Atoi("AAA")
	fmt.Println(n2, err) // 0 strconv.Atoi: parsing "AAA": invalid syntax
}

进程信息

import (
	"fmt"
	"os"
	"os/exec"
)

func main() {
	// go run example/20-env/main.go a b c d
//命令行参数
	fmt.Println(os.Args)           // [/var/folders/8p/n34xxfnx38dg8bv_x8l62t_m0000gn/T/go-build3406981276/b001/exe/main a b c d]
//获取环境变量
	fmt.Println(os.Getenv("PATH")) // /usr/local/go/bin...
	fmt.Println(os.Setenv("AA", "BB"))

//启动子进程并获取输入输出
	buf, err := exec.Command("grep", "127.0.0.1", "/etc/hosts").CombinedOutput()
	if err != nil {
		panic(err)
	}
	fmt.Println(string(buf)) // 127.0.0.1       localhost
}