基础语法与简单实战 | 青训营笔记

84 阅读6分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记

在得知自己通过笔试后,自己便开始提前学习了一会go语言,以便于自己在开营后能跟上学习进度。所以本篇笔记中代码部分均来自于我自己在观看学习了这个b站视频后www.bilibili.com/video/BV1gf…

自己所写的代码。

GO语言的简单配置

go语言的环境变量的配置,和其他语言相差不大。

主要是配置GOROOT,GOPATH这两个变量

GOROOT

GOROOT就是go语言在你电脑上安装的根目录

image.png 我们直接在系统变量里新建一个环境变量即可

GOPATH

GOPATH相对于是go语言的工作区,在GOPATH下必须包含 pkg, bin, src等文件夹。当我们使用gopath模式创建项目时,必须将源码保存到src文件夹下。(不过现在开发一般推荐GOModule形式) 同样的我们新建一个系统变量和用户变量,将GOPATH目录配置完成即可。

image.png

简单的hello world实例

package main //程序的包名

import(
	"fmt"
)
//main函数
func main() { //花括号不换行
	fmt.Printf("hello world!!!") //可以加分号,建议不加
}

可以使用go run 运行 main.go文件 使用go build 编译文件 然后使用./main运行

基础语法

变量和常量

Golang中变量声明方式有多种

var a int //不初始化默认为0
var a int = 1
var a = 1
a := 1
-------------
//golang常量没有确定的类型可以通过上下文推断类型
//常量 只读
const length int = 180

//const 可以定义枚举类型

const (
   // 可以在const()添加一个关键字iota,每行的iota都会累加1,第一行的iota的默认值为0
   BEIJING = 10 * iota
   SHANGHAI
   ZHIJIANG
)

常用方法

if else

和c/c++,java的区别在于没有括号

if a != nil{
}

for

//死循环
for {
}

for i := 0; i < n; i ++ {
}

i := 1
for i <= 3 {
    i ++
}

switch

除开不用加break,其他同c/c++一致

a := 1
switch a{
    case 1:
        fmt.Println("1")
    default:
        fmt.Println("x")
}

t := 1
switch a{
    //可以使用布尔语句
    case t < 12:
        fmt.Println("1")
    default:
        fmt.Println("x")
}

数组和slice

数组

固定容量

//固定长度的数组
var myArray1 [10] int

slice

声明

//声明slice1是切片,初始长度为3, 默认值为1, 2, 3
slice1 := []int{1, 2, 3}
fmt.Printf("len = %d, slice1 = %v\n", len(slice1), slice1)
//声明slice2是一个切片,但是没有给slice2分配空间
var slice2 []int
slice2 = make([]int, 3) //开辟三个空间,默认值为0
fmt.Printf("len = %d, slice2 = %v\n", len(slice2), slice2)
slice3 := make([]int, 3) //通过:=推导出来为切片
fmt.Printf("len = %d, slice1 = %v\n", len(slice3), slice3)
var slice4 []int
if slice4 == nil{
    fmt.Println("为空")
}else{
    fmt.Println("不为空")
}

追加

package main

import "fmt"

func main(){
	var numbers = make([]int, 3, 5)

	//向numbers切片追加一个元素1, number len = 4, [0,0,0,1], cap = 5
	numbers = append(numbers, 1)
	fmt.Printf("len = %d, cap = %d\n", len(numbers), cap(numbers))

	//向numbers切片追加一个元素1, number len = 4, [0,0,0,1], cap = 5
	numbers = append(numbers, 2)
	fmt.Printf("len = %d, cap = %d\n", len(numbers), cap(numbers))
	//向cap已满的切片中加入一个新元素,新的cap = 2 * 旧的cap
	numbers = append(numbers, 3)
	fmt.Printf("len = %d, cap = %d\n", len(numbers), cap(numbers))
	fmt.Printf("-----------------\n")
	//不规定cap,默认和len相等
	var n1 = make([]int, 3)
	fmt.Printf("len = %d, cap = %d\n", len(n1), cap(n1))
	n1 = append(n1, 1)
	fmt.Printf("len = %d, cap = %d\n", len(n1), cap(n1))
}

截取

切片默认执行的是一块内存空间,即s1 = s2[1:] 修改s1,s2也会改变

package main


import "fmt"


func main() {
	/* 创建切片 */
	numbers := []int{0,1,2,3,4,5,6,7,8}
	printSlice(numbers)


	/* 打印原始切片 */
	fmt.Println("numbers ==", numbers)


	/* 打印子切片从索引1(包含) 到索引4(不包含)*/
	fmt.Println("numbers[1:4] ==", numbers[1:4])


	/* 默认下限为 0*/
	fmt.Println("numbers[:3] ==", numbers[:3])


	/* 默认上限为 len(s)*/
	fmt.Println("numbers[4:] ==", numbers[4:])


	numbers1 := make([]int,0,5)
	printSlice(numbers1)


	/* 打印子切片从索引  0(包含) 到索引 2(不包含) */
	number2 := numbers[:2]
	printSlice(number2)


	/* 打印子切片从索引 2(包含) 到索引 5(不包含) */
	number3 := numbers[2:5]
	printSlice(number3)


}


func printSlice(x []int){
	fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}

copy

package main

import "fmt"

func main(){
	s := []int{1,2,3}

	//copy可以将底层数组的slice一起进行拷贝
	s2 := make([]int, 3)
	copy(s2, s)
	s2[0] = 3
	fmt.Println(s[0])
}

slice本质是指向s头的指针,所以copy后修改copy后的切片会修改原切片

Map

Map的四种声明方式

package main

import "fmt"

func main(){
	//===>第一种声明方式
	//声明myMap1是一种map类型 key是string, value是string
	var myMap1 map[string]string
	if (myMap1 == nil){
		fmt.Println("myMap1是一个空map")
	}

	//使用map前使用make分配数据空间
	myMap1 = make(map[string]string, 10)
	myMap1["one"] = "java"
	myMap1["two"] = "c++"
	myMap1["three"] = "python"
	fmt.Println("myMap1 = ", myMap1)
	//===>第二种声明方式
	myMap2 := make(map[int]string)
	myMap2[1] = "java"
	myMap2[2] = "c++"
	myMap2[3] = "python"

	fmt.Println(myMap2)

	//===>第三种声明方式
	myMap3 := map[string]string{
		"one" : "php",
		"two" : "c++",
		"three" : "python",
	}
	fmt.Println(myMap3)

}

Map的使用

package main

import "fmt"

func printMap(cityMap map[string]string){
	//引用传递
	//遍历
	for key, value := range cityMap{
		fmt.Println("key = ", key, "value = ", value)
	}
}


func main(){
	cityMap := make(map[string]string)

	//添加
	cityMap["China"] = "Beijing"
	cityMap["Japan"] = "Tokyo"
	cityMap["USA"] = "NewYork"

	//遍历
	printMap(cityMap)

	//删除
	delete(cityMap, "Japan")

	//遍历
	printMap(cityMap)

	//修改
	cityMap["USA"] = "DC"

	//遍历
	printMap(cityMap)



}

函数

Golang中的函数支持返回多个值,并且返回类型后置,这是和其他语言有明显差别的地方

package main

import "fmt"

func foo1(a string, b int) int {
	fmt.Println(a)
	fmt.Println(b)

	c := 100
	return c
}

func foo2() (int, int) {
	return 888, 999
}

func foo3() (r1 int, r2 int) {
	//r1 r2属于foo3的形参 初始默认值为0
	//r1 r2作用域空间 是foo3 整个函数体内部的空间
	r1 = 1000
	r2 = 2000
	return
}


func foo4() (r1, r2 int){
	r1 = 1
	r2 = 2
	return
}
func main(){
	a := foo1("ava", 520)
	fmt.Println(a)
	b, c := foo2()
	fmt.Println(b ,c)
	e, f := foo3()
	fmt.Println(e, f)
	g, h := foo4()
	fmt.Println(g, h)
}

指针

Golang中的指针并不像c/c++中的那样的复杂

主要掌握,* 和 & 的用法 即可,其中的参数中的值传递和引用传递和其他语言相差不多。

package main

import "fmt"

//值传递
func swap(a int, b int){
	var temp int
	a = b
	b = temp
}
//引用传递
func swap1(a *int, b *int){
	var temp int
	temp = *a
	*a = *b
	*b = temp
}

func main(){
	var a int = 10
	var b int = 20
	swap1(&a, &b)
	fmt.Println(a, b)

	var p *int
	p = &a
	fmt.Println(p)
	fmt.Println(&a)
        
        //指针的指针
	var pp **int
	pp = &p
	fmt.Println(&p)
	fmt.Println(pp)

}

结构体

Golang中我们可以为结构体实现一些结构体方法,这类似于Java中的类方法。我们也可以将strut近似的看为一个“类”

结构体的基本创建和使用

package main

import "fmt"

type myint int

type Book struct {
	id int
	name string
}

func printBook(book Book){
	//值传递
	fmt.Printf("book = %d, name = %v\n", book.id, book.name)
}

func printBook1(book *Book){
	//引用传递
	book.id = 2
	fmt.Printf("book = %d, name = %v\n", book.id, book.name)
}

func main(){
	var a myint = 10
	fmt.Println("a = ", a)
	var book1 Book
	book1.id = 1
	book1.name = "xxx"
	printBook(book1)
	printBook1(&book1)
}

错误处理

Golang中没有异常的存在,Golang中所有的运行报错都是错误。 我们可以直接在方法的返回值中返回一个err,让我们能够直接使用if进行错误的捕获。

结构体标签TAG

我们可以使用结构体标签来对结构进行标记,设定一些信息,比如定义其转换成json时对应的名称

package main

import (
	"fmt"
	"reflect"
)

type resume struct {
	Name string `info:"name" doc:"我的名字" json:"name"`
	Sex string `info:"sex" json:"sex"`

}

func findTag(str interface{}){
	t := reflect.TypeOf(str).Elem()
	for i := 0; i < t.NumField(); i ++ {
		tagInfo := t.Field(i).Tag.Get("info")
		tagDoc := t.Field(i).Tag.Get("doc")
		fmt.Println("info = ", tagInfo)
		fmt.Println("doc = ", tagDoc)
	}
}

func main() {
	var re resume
	findTag(&re)
}

实战小栗子🌰

猜数字游戏

功能分析:

  • 需要获到一个0~100的一个随机数
  • 获取用户输入的数
  • 比较并判断结果
  • 相同则游戏结束
  • 不同则给出提示 并循环

我自己增加了一个简单的扩展,限制玩家的猜测次数 代码本身没什么难点,主要是使用bufio包进行读入比较繁琐

package main

import (
   "bufio"
   "fmt"
   "math/rand"
   "os"
   "strconv"
   "strings"
   "time"
)

func main() {
   //生成随机数
   maxNum := 100
   //最多猜测次数
   maxCnt := 10
   //设置随机数种子
   rand.Seed(time.Now().UnixNano())
   //获取随机数
   ansNum := rand.Intn(maxNum)
   //需要重置随机数种子否则结果一直为相同的随机数
   //fmt.Println("ansNum = ", ansNum)

   //简单的扩展:增加一个玩家猜测次数和限制玩家猜测次数
   cnt := 0
   
   //循环游戏
   for cnt < maxCnt{
      cnt ++
      //读入数字
      fmt.Println("Please input your guess:")
      reader := bufio.NewReader(os.Stdin)
      input, err := reader.ReadString('\n')
      if err != nil{
         fmt.Println("err! Please try again!", err)
         return
      }
      input = strings.TrimSuffix(input, "\n")
      guess, err := strconv.Atoi(input)
      if err != nil{
         fmt.Println("err! Please try again!", err)
         return
      }
      fmt.Println("You guess is", guess)

      if guess > ansNum {
         fmt.Println("Your guess is bigger than ansNum. Please try again")
      }else if guess < ansNum {
         fmt.Println("Your guess is smaller than ansNum. Please try again")
      }else {
         fmt.Println("Correct, you legend, your guess the number is ", cnt)
         break
      }

   }
}

命令行字典

彩云翻译 fanyi.caiyunapp.com/ 进去以后我们随便输入一个单词,翻译,按F12打开开发者工具,选择网络找到dict请求(类型为POST) 复制一下请求包的信息

image.png

复制到下面的网站,自动生成代码 curlconverter curlconverter.com/#go

解析response body 代码生成 oktools.net/json2go

代码有一点点长

项目的难点主要是使用代码生成器生成代码并使用。

package main

import (
   "bytes"
   "encoding/json"
   "fmt"
   "io/ioutil"
   "log"
   "net/http"
   "os"
)

type DictRequest struct {
   TransType string `json:"trans_type"`
   Source string `json:"source"`
   UserId string `json:"user_id"`
}

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 main() {

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

}

func query(word string){
   client := &http.Client{}
   //创建请求
   //自定义data

   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("Accept", "application/json, text/plain, */*")
   req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
   req.Header.Set("Connection", "keep-alive")
   req.Header.Set("Content-Type", "application/json;charset=UTF-8")
   req.Header.Set("Origin", "https://fanyi.caiyunapp.com")
   req.Header.Set("Referer", "https://fanyi.caiyunapp.com/")
   req.Header.Set("Sec-Fetch-Dest", "empty")
   req.Header.Set("Sec-Fetch-Mode", "cors")
   req.Header.Set("Sec-Fetch-Site", "cross-site")
   req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36")
   req.Header.Set("X-Authorization", "token:qgemv4jr1y38jyq6vhvi")
   req.Header.Set("app-name", "xy")
   req.Header.Set("os-type", "web")
   req.Header.Set("sec-ch-ua", `" Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100"`)
   req.Header.Set("sec-ch-ua-mobile", "?0")
   req.Header.Set("sec-ch-ua-platform", `"Windows"`)
   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))
   }

   //解析response body
   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)
   }
}

SOCKS5代理