Golang学习 | 青训营笔记

134 阅读6分钟

语法

switch-case

  • go的switch-case语句自带break。
  • 跨越case可以用在句尾fallthrough
  • switch后可以为空,case后面可以是表达式

slice

相比数组更为灵活

//s 为长度为3的string类型
s := make([]string, 3)
s.append(s, "d")

//需要保证c有足够的空间才能复制
c := make([]string, len(s))
copy(c, s)

map

相当于C++中的unordered_map,输出顺序是不确定的,和其使用的哈希函数有关。

m := make(map[string]int)
m["one"] = 1
r, ok := m["one"]
delete(m, "one")

point

传参时如果要改变参数的值需要传递指针,否则传的参数为副本不能更改原值。对于较大的结构体变量也可以传指针参数节省拷贝副本的成本。

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

struct

type user struct {
	name     string
	password string
}
a := user{name: "wang", password: "1024"}
//类成员函数:func后面加上括号
func (u *user) resetPassword(password string) {
	u.password = password
}

error

//error类型
func findUser(users []user, name string) (v *user, err error) {
	for _, u := range users {
		if u.name == name {
			return &u, nil
		}
	}
	//出现错误就返回nil并用errors创建一个错误信息的字段
	return nil, errors.New("not found")
}

string

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
//一个中文3个char,utf-8
b := "你好"
fmt.Println(len(b)) // 6

json

包:"encoding/json",json解析需要结构体字段一一对应,需要保证结构体中的字段都是大写开头

type userInfo struct {
	Name string
	//输出的字段设置为小写需要加上tag
	Age   int `json:"age"`
	Hobby []string
}

a := userInfo{Name: "wang", Age: 18, Hobby: []string{"Golang", "TypeScript"}}
//对结构体信息生成json类型格式的文件
buf, err := json.Marshal(a)
fmt.Println(buf)         // [123 34 78 97...]
fmt.Println(string(buf)) // {"Name":"wang","age":18,"Hobby":["Golang","TypeScript"]}
//序列化
buf, err = json.MarshalIndent(a, "", "\t")
fmt.Println(string(buf)) //输出结构化的json object
var b userInfo
//反序列化:把json放入结构体变量b中
var b userInfo
err = json.Unmarshal(buf, &b)
fmt.Printf("%#v\n", b) // main.userInfo{Name:"wang", Age:18, Hobby:[]string{"Golang", "TypeScript"}}

strconv

包strconv实现了对基本数据类型的字符串表示的转换。

i, err := strconv.Atoi("-42")
s := strconv.Itoa(-42)

Parse 系列函数用于将字符串转换为指定类型的值,其中包括 ParseBool()、ParseFloat()、ParseInt()、ParseUint()。

  • ParseBool()函数用于将字符串转换为 bool 类型的值,它只能接受 1、0、t、f、T、F、true、false、True、False、TRUE、FALSE,其它的值均返回错误
  • ParseInt()函数用于返回字符串表示的整数值(可以包含正负号)
  • ParseUnit()函数的功能类似于 ParseInt() 函数,但 ParseUint() 函数不接受正负号,用于无符号整型
  • ParseFloat() 函数用于将一个表示浮点数的字符串转换为 float 类型。
func ParseBool(str string) (value bool, err error)
func ParseInt(s string, base int, bitSize int) (i int64, err error)
func ParseUint(s string, base int, bitSize int) (n uint64, err error)
func ParseFloat(s string, bitSize int) (f float64, err error)

工程实践

并发和并行

  • 并发:多线程程序在一个核上运行
  • 并行:多线程在多个核上运行
  • 协程轻量级线程,在用户态
  • 线程在内核态
func hello(i int) {
	println("hello goroutine : " + fmt.Sprint(i))
}

//协程
func HelloGoRoutine() {
	for i := 0; i < 5; i++ {
		go func(j int) {
			hello(j)
		}(i)
	}
	time.Sleep(time.Second)	//主线程执行完之前不退出,这里可以优化
}

使用WaitGroup优化

func ManyGoWait() {
    var wg sync.WaitGroup
    wg.Add(5)				//5个协程
    for i := 0; i< 5; i++ {
    	go func(j int) {
        	defer wg.Done() //结束该协程之后计数器减一
            hello(j)
        }(i)
    }
    wg.Wait()		//阻塞
}

通道

//make(chan 元素类型[,缓冲大小])
src := make(chan int)
dest := make(chan int, 3)
  • A子协程发送数字0-9:无缓冲通道又称为同步通道
go func() {
	defer close(src)
	for i := 0; i < 10; i++ {
		src <- i
	}
}()
  • B子协程计算输入数字的平方: 有缓冲通道,典型的生产消费模型,消费者可能慢一些,有缓冲可以解决生产消费之间不平衡问题
go func() {
	defer close(dest)
	for i := range src {
		dest <- i * i
	}
}()
//输出
for i := range dest {
	//复杂操作
	println(i)
}

并发安全

var (
	x    int64
	lock sync.Mutex //互斥锁
)
  • 加锁操作使得在进入函数体时访问临界区变量是安全的,当然这是有成本的。
func addWithLock() {
	for i := 0; i < 2000; i++ {
		lock.Lock()
		x += 1
		lock.Unlock()
	}
}
  • 不加锁
func addWithoutLock() {
	for i := 0; i < 2000; i++ {
		x += 1
	}
}
  • 分别测试两种情况
x = 0
for i := 0; i < 5; i++ {
	go addWithoutLock()
}
time.Sleep(time.Second)

x = 0
for i := 0; i < 5; i++ {
	go addWithLock()
}
time.Sleep(time.Second)

依赖管理

  • GOPATH 项目代码直接依赖src下的代码,go get 下载最新版本的包到src目录下。GOPATH无法实现package的多版本控制。
  • Go Vender 项目目录下添加一个vender文件夹,通过每隔项目引入一份依赖的副本,解决了多个项目需要同一个package依赖的冲突问题。
  • Go Module 通过 go.mod 文件管理依赖包版本,通过 go get/go mod指令工具管理依赖包

字典查询项目

接口使用彩云小译

  1. 创建保存json数据的对应实体,json与结构体转换
  2. http报文的解析:curl转go代码
  3. 使用命令行运行
type DictRequest struct {
	TransType string `json:"trans_type"`
	Source    string `json:"source"`
	UserID    string `json:"user_id"`
}

//查询出来的json字段需要保存到对应的结构体中
type DictResponse struct {
	Rc   int `json:"rc"`
	Wiki struct {
		KnownInLaguages int `json:"known_in_laguages"`
		Description     struct {
			Source string      `json:"source"`
			Target interface{} `json:"target"`
		} `json:"description"`
		ID   string `json:"id"`
		Item struct {
			Source string `json:"source"`
			Target string `json:"target"`
		} `json:"item"`
		ImageURL  string `json:"image_url"`
		IsSubject string `json:"is_subject"`
		Sitelink  string `json:"sitelink"`
	} `json:"wiki"`
	Dictionary struct {
		Prons struct {
			EnUs string `json:"en-us"`
			En   string `json:"en"`
		} `json:"prons"`
		Explanations []string      `json:"explanations"`
		Synonym      []string      `json:"synonym"`
		Antonym      []string      `json:"antonym"`
		WqxExample   [][]string    `json:"wqx_example"`
		Entry        string        `json:"entry"`
		Type         string        `json:"type"`
		Related      []interface{} `json:"related"`
		Source       string        `json:"source"`
	} `json:"dictionary"`
}
  • 接口
func query(word string) {
	client := &http.Client{}
	request := DictRequest{TransType: "en2zh", Source: word}
	buf, err := json.Marshal(request)
	if err != nil {
		log.Fatal(err)
	}
	var data = bytes.NewReader(buf)
	req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
	if err != nil {
		log.Fatal(err)
	}
	req.Header.Set("Connection", "keep-alive")
	req.Header.Set("DNT", "1")
	req.Header.Set("os-version", "")
	req.Header.Set("sec-ch-ua-mobile", "?0")
	req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36")
	req.Header.Set("app-name", "xy")
	req.Header.Set("Content-Type", "application/json;charset=UTF-8")
	req.Header.Set("Accept", "application/json, text/plain, */*")
	req.Header.Set("device-id", "")
	req.Header.Set("os-type", "web")
	req.Header.Set("X-Authorization", "token:qgemv4jr1y38jyq6vhvi")
	req.Header.Set("Origin", "https://fanyi.caiyunapp.com")
	req.Header.Set("Sec-Fetch-Site", "cross-site")
	req.Header.Set("Sec-Fetch-Mode", "cors")
	req.Header.Set("Sec-Fetch-Dest", "empty")
	req.Header.Set("Referer", "https://fanyi.caiyunapp.com/")
	req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
	req.Header.Set("Cookie", "_ym_uid=16456948721020430059; _ym_d=1645694872")
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	bodyText, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	//确定状态码正确才运行
	if resp.StatusCode != 200 {
		log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))
	}
	var dictResponse DictResponse
	err = json.Unmarshal(bodyText, &dictResponse)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(word, "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)
	//输出信息
	for _, item := range dictResponse.Dictionary.Explanations {
		fmt.Println(item)
	}
}

func main() {
	if len(os.Args) != 2 {
		fmt.Fprintf(os.Stderr, `usage: simpleDict WORD
example: simpleDict hello
		`)
		os.Exit(1)
	}
	word := os.Args[1]
	query(word)
}