Go语言快速上手笔记(一) | 青训营笔记

98 阅读10分钟

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

对比Java学习golang

一、基础语法

1.入门程序HelloWorld

java代码

package test;
​
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("HelloWorld");
    }
}

go代码

//标记为程序的入口包,也就是程序的入口文件
package main
​
import (
    //fmt主要向屏幕输入输出字符串,格式化字符串
    "fmt"
)
​
func main() {
    //调用fmt中Println来输出HelloWorld
    fmt.Println("HelloWorld")
}

由两个入门案例的对比

  1. java强制要求main方法所在的类名和文件名保持一致,而共语言没有这种要求
  2. import导包,我觉得还是有相似之处的,由于java将java.lang包默认导入,因此在使用System.out.println没有导包,而go需要显性写出来

package讲解

fmt用法

2.编译、运行

java使用命令行的方式编译运行

javac main.class

java main

go编译运行

go build main.go

./main

也可以使用

go run main.go

3.变量

go

package main
​
import (
   "fmt"
   "math"
)
​
func main() {
   var a string = "initial"
   //类型推断,省略变量类型
   var b, c = 4, a + "1"
   var d = true
   var e float64
   // := 再次省略var关键字
   f := float32(e)
   g := a + c
   fmt.Println(a, b, c, d, e, f) //initial 4 initial1 true 0 0
   fmt.Println(g)
​
   //直接定义常量
   const s = "const"
   const h = 20000
   //3e2 = 3*10^2
   const i = 3e2 / h
   fmt.Println(s, h, i)
   //调用math中的Ceil求出i的向上取整结果值
   fmt.Println(math.Ceil(i),math.Sin(i)) //1  0.02999550020249566 
}

java

String a = "initial",c = a + "1";
int b = 4;
boolean d = true;
float f = 0;
String g = a + c;
​
常量在java中的声明为
public static final int s = "const";

4.if-else

go

package main
​
import "fmt"
​
func main() {
    //与java有所不同的是:1. 条件不需要加括号;2. 强制if/else后面使用{},java不强制但是建议也使用{},便于阅读
   if 7%2 == 0 {
      fmt.Println("7 is even")
   } else {
      fmt.Println("7 is odd")
   }
​
   //在此处定义的变量,其作用范围也在if中,出了if之后就不能使用
   if num := 9; num < 0 {
      fmt.Println(num, "is negative")
   } else if num < 10 {
      fmt.Println(num, "his 1 digit")
   }else {
      fmt.Println(num)
   }
}

java

if (7 % 2 == 0) {
    System.out.println("7 is even");
} else {
    System.out.println("7 is odd");
}
​
int num = 0;
if (num < 0) {
    System.out.println(num + "is negative");
} else if (num < 10) {
    System.out.println(num +  "his 1 digit");
}else {
    System.out.println(num);
}

5.循环

go没有while,do-while循环,只有for循环

func main() {
   i := 1
   //不写条件,也就代表死循环
   for {
      fmt.Printf("%d ", i)
      i++
      if i == 10 {
         break
      }
   }
   fmt.Println()
   for j := 7; j < 10; j++ {
      fmt.Printf("%d ", j)
   }
}

java

int i = 1;
//不写条件,也就代表死循环
for (;;) {
    System.out.print(i + " ");
    i++;
    if (i == 10) {
        break;
    }
}
System.out.println();
for (int j = 7; j < 10; j++) {
    System.out.print(j + " ");
}

6.Switch

go

package main
​
import (
   "fmt"
   "time"
)
​
func main() {
   a := 2
   switch a {
   case 1:
      fmt.Println(1)
   case 2:
      fmt.Println(2)
   case 3:
      fmt.Println(3)
   case 4,5:
      fmt.Println(4,"or",5)
   default:
      fmt.Println("other")
   }
​
   t := time.Now()
   switch {
   case t.Hour() < 12:
      fmt.Println("早上")
   default:
      fmt.Println("下午")
   }
   fmt.Println(time.Now().Format(time.UnixDate))
}

java

int i = 1;
switch (i){
    case 1:
        System.out.println(1);
        break;
    case 2:
        System.out.println(2);
        break;
    case 3:
    case 4:
        System.out.println(3 + " or " + 4);
        break;
    default:
        System.out.println("other");
}
​
Date date = new Date();
if (date.getHours() < 12){
    System.out.println("早上");
}else {
    System.out.println("下午");
}
  1. 其中,java的switch有以下注意事项

最早时,只支持int、char、byte、short这样的整型的基本类型或对应的包装类型Integer、Character、Byte、Short常量,包装类型最终也会经过拆箱为基本类型,本质上还是只支持基本类型

JDK1.5开始支持enum,原理是给枚举值进行了内部的编号,进行编号和枚举值的映射

JDK1.7开始支持String,但不允许为null,原理是借助 hashcode( ) 来实现。

  1. 并且在case结束后需要加上break,不然后执行下一个case,而go语言不会有这种担忧

7.数组

go

package main
​
import (
   "fmt"
)
​
func main() {
   var a [5]int
   a[4] = 100
   fmt.Println(a[4],len(a))
​
   b := [5]int{1,2,3,4,5}
   fmt.Println(b)
​
   var c [2][3]int
   for i := 0; i < 2; i++ {
      for j := 0; j < 3; j++ {
         c[i][j] = i*j;
      }
   }
   fmt.Println(c)
}

java

int[] a = new int[5];
a[4] = 100;
System.out.println(a[4] + "  " + a.length);
​
int[] b = {1, 2, 3, 4, 5};
System.out.println(Arrays.toString(b));
​
int[][] c = new int[2][3];
for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 3; j++) {
        c[i][j] = i * j;
    }
}
for (int i = 0; i < 2; i++) {
    System.out.println(Arrays.toString(c[i]));
}

8.切片

go常用切片代替数组

package main
​
import "fmt"func main(){
   s := make([]string,3)
   s[0] = "a"
   s[1] = "b"
   s[2] = "c"
   fmt.Println(s[2])
   fmt.Println(len(s))
​
   s = append(s, "d")
   s = append(s,"e","f")
   fmt.Println(s)
​
   c := make([]string,len(s))
   copy(c,s)
​
   //[2,5)
   fmt.Println(s[2:5])
   fmt.Println(s[:3])
   fmt.Println(s[3:])
}

9.Map

go

package main
​
import "fmt"func main() {
   m := make(map[string] int)
   m["one"] = 1
   m["two"] = 2
   fmt.Println(m)
   fmt.Println(len(m))
   fmt.Println(m["one"])
   fmt.Println(m["unknow"])
​
   r,ok := m["unknow"]
   fmt.Println(r,ok)
​
   delete(m,"one")
   m2 := map[string]int {"one":1,"two":2}
   var m3 = map[string]int {"one":1,"two":2}
   fmt.Println(m2,m3)
​
}

java

Map<String,Integer> map = new HashMap<>();
map.put("one",1);
map.put("two",2);
System.out.println(map);
System.out.println(map.size());
System.out.println(map.get("one"));
System.out.println(map.get("unknow"));
​
map.remove("one");

10.range

go

package main
​
import "fmt"func main() {
   nums := []int{1, 2, 3}
   sum := 0
   for i, num := range nums {
      sum += num
      if i == 2 {
         break
      }
   }
   fmt.Println(sum)
​
   m := map[string]string{"a": "A", "b": "B"}
   for k,v := range m{
      fmt.Println(k,v)//a A;b B
   }
​
   for k := range m {
      fmt.Println("key",k)//key a;key b
   }
​
   for _,v := range m {
      fmt.Println("value",v)//value A;value B
   }
}

java

int[] nums = new int[]{1,2,3};
int sum = 0;
for (int num : nums) {
    sum += num;
}
​
Map<String,String> map = new HashMap<>();
map.put("a","A");
map.put("b","B");
System.out.println("map = " + map);
//与forEach类似
map.forEach(
        (k,v)->{
            System.out.println(k + " : " + v);
        }
);
​
Set<String> keys = map.keySet();
System.out.println("keys = " + keys);
Collection<String> values = map.values();
System.out.println("values = " + values);

11.函数

go

package main
​
import "fmt"func main() {
   res := add(1, 2)
   fmt.Println(res)
   m := map[string]string{"a":"A"}
   fmt.Println(exists(m,"a"))
}
​
func add(a int, b int) int {
   return a + b
}
​
func add1(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
}

java只能返回一个值,当然,可以把多个返回值做一个类,或者返回一个容器

private static int add(int a, int b) {
    return a + b;
}
​
private static Map<String, Boolean> exists(Map<String, String> m, String k) {
    String s = m.get(k);
    if (s == null){
        Map map =  new HashMap<>();
        map.put(null,false);
        return map;
    }else {
        Map map =  new HashMap<>();
        map.put(map.get(k),true);
        return map;
    }
}

12.指针

package main
​
import "fmt"func main() {
   n := 5
   add2(n)
   fmt.Println(n)
   add2ptr(&n)
   fmt.Println(n)
}
func add2(n int) {
   n += 2
}
func add2ptr(n *int) {
   *n += 2
}

13.结构体

package main
​
import "fmt"type user struct{
   name string
   password string
}
​
func main(){
   a := user{name:"zhangsan",password: "111111"}
   b := user{"zhangsan","111111"}
   c := user{name:"zhangsan"}
   c.password = "111111"
   var d user
   d.name = "zhangsan"
   d.password = "111111"
​
   fmt.Println(a,b,c,d)
   fmt.Println(checkPassword(a,"111111"))
   fmt.Println(checkPassword2(&a,"123456"))
​
}
​
func checkPassword(u user,password string) bool{
   return u.password == password
}
​
func checkPassword2(u *user,password string) bool{
   return u.password == password
}

14.结构体方法

package main
​
import "fmt"type user struct {
    name     string
    password string
}
​
func main() {
    b := user{"zhangsan", "111111"}
    //成为了结构体方法,就用“结构体.方法名”调用
    b.resetPassword("123456")
    fmt.Println(b.checkPassword("111111"))
​
}
​
func (u user) checkPassword(password string) bool {
    return u.password == password
}
//带指针就可以对这个结构体进行修改
func (u *user) resetPassword(password string) {
    u.password = password
}

15.错误处理

package main
​
import (
   "errors"
   "fmt"
)
​
​
type user struct{
   name string
   password string
}
​
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{{"zhangsan", "111111"}},"zhangsan")
   if err != nil {
      fmt.Println(err)
      return
   }
   fmt.Println(u.name)
​
   if u, err := finduser([]user{{"zhangsan", "123456"}}, "zhang");err ==nil {
      fmt.Println(u.name)//not found
   }else {
      fmt.Println(err)
      return
   }
}

不同于java的异常处理,go的异常以返回值的形式抛出,需要调用者判断是都发生了异常

16.字符串操作

package main
​
import (
   "fmt"
   "strings"
)
​
func main() {
   a := "hello"
   //字符串a中是否存在"ll“
   fmt.Println(strings.Contains(a,"ll"))
   //字符串a中存在几个"l“
   fmt.Println(strings.Count(a,"l"))
   //字符串a中是否以"He“开头
   fmt.Println(strings.HasPrefix(a,"He"))
   //字符串a中是否以"lo“结尾
   fmt.Println(strings.HasSuffix(a,"lo"))
   //字符串a中"o“第一次出现的位置
   fmt.Println(strings.Index(a,"o"))
   //字符串拼接
   fmt.Println(strings.Join([]string{"a","b","a"},"-"))
   //重复3次a字符串
   fmt.Println(strings.Repeat(a,3))
   //替换3次a字符串,当最后一个值小于0,就代表不受次数限制
   fmt.Println(strings.Replace(a,"l","n",3))
   //根据"-"分割字符串并返回成数组
   fmt.Println(strings.Split("a-b-c-d","-"))
   //转换为全小写
   fmt.Println(strings.ToLower(a))
   //转换为全大写
   fmt.Println(strings.ToUpper(a))
   
   //内置函数
   //字符串长度,一个中文可能对应多个字符
   fmt.Println(len(a))
}

17.字符串格式化

package main
​
import "fmt"type point struct{
   x,y int
}
​
func main() {
   s := "hello"
   n := 123
   p := point{1,2}
   fmt.Println(s,n)
   fmt.Println(p)
​
   fmt.Printf("s=%v\n",s)
   fmt.Printf("n=%v\n",n)
   fmt.Printf("p=%v\n",p)
   fmt.Printf("p=%+v\n",p)
   fmt.Printf("p=%#v\n",p)
​
   f := 3.1415926
   fmt.Println(f)
   fmt.Printf("%.2f",f)//3.14
​
}

18.json处理

package main
​
import (
    "encoding/json"
    "fmt"
)
​
type userInfo struct {
    //结构体中的字段以大写开头,就可以使用json序列化
    Name  string
    //字段名改为"age"
    Age   int `json:"age"`
    Hobby []string
}
​
func main() {
    a := userInfo{"zhangsan", 21, []string{"java", "go"}}
    //序列化为byte数组
    buf, err := json.Marshal(a)
    if err != nil {
        panic(err)
    }
    fmt.Println(buf)
    //类型转化为字符串
    fmt.Println(string(buf))
​
    buf, err = json.MarshalIndent(a, "", "\t")
    if err != nil {
        panic(err)
    }
    fmt.Println(string(buf))
​
    var b userInfo
    //反序列化到一个空变量里
    err = json.Unmarshal(buf,&b)
    if err != nil {
        panic(err)
    }
    fmt.Printf("%#v\n",b)
​
}
​

image-20220507170206091

19.时间处理

package main
​
import (
   "fmt"
   "time"
)
​
func main() {
   now := time.Now()
   fmt.Println(now)//2022-05-07 18:33:16.8002889 +0800 CST m=+0.011001801
   t := time.Date(2022,5,7,1,18,10,0,time.UTC)
   t2 := time.Date(2022,5,7,2,10,30,0,time.UTC)
   fmt.Println(t)//2022-05-07 01:18:10 +0000 UTC
   fmt.Println(t.Year(),t.Month(),t.Day(),t.Hour(),t.Minute())//2022 May 7 1 18
    //格式化时间,特定的时间2006-01-02 15:04:05
   fmt.Println(t.Format("2006-01-02 15:04:05"))//2022-05-07 01:18:10
   diff := t2.Sub(t)
   fmt.Println(diff)//52m20s
   fmt.Println(diff.Minutes(),diff.Seconds())//52.333333333333336 3140
​
   t3,err := time.Parse("2006-01-02 15:04:05","2022-05-07 18:47:55")
   if err != nil {
      panic(err)
   }
   fmt.Println(t3 == t)//false
   fmt.Println(t3)
    //获取时间戳
   fmt.Println(now.Unix())//1651920422
​
}

运行结果: image-20220507170206091.png

20.数字解析

package main
​
import (
   "fmt"
   "strconv"
)
​
func main() {
   f,_ := strconv.ParseFloat("1.2345",64)
   fmt.Println(f)//1.2345
​
   //第二位代表传入的是10进制(0代表自动推测),第三位表示返回一个64位整数
   n,_ := strconv.ParseInt("111",8,64)
   fmt.Println(n)//73
​
   n2,_ := strconv.Atoi("123")
   fmt.Println(n2)//123
​
   n2,err := strconv.Atoi("AAA")
   fmt.Println(n2,err)//0 strconv.Atoi: parsing "AAA": invalid syntax
​
}

21.进程信息

package main
​
import (
   "fmt"
   "os"
   "os/exec"
)
​
func main() {
   //获取命令行参数
   fmt.Println(os.Args)
   //获取环境变量
   fmt.Println("PATH=",os.Getenv("PATH"))
   //设置环境变量
   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(buf)
}

二、实战

1.猜数字游戏

生成0-100之间的一个随机数

每次玩家输入一个数字,程序都会告诉是大于还是小于猜的这个数

直到猜对了,退出游戏

package main
​
import (
   "bufio"
   "fmt"
   "math/rand"
   "os"
   "strconv"
   "strings"
   "time"
)
​
func main() {
   maxNum := 100
   //用时间戳初始化随机数种子
   rand.Seed(time.Now().Unix())
   secretNumber := rand.Intn(maxNum)//81
   //fmt.Println("The serect number is ",secretNumber)
​
   fmt.Println("Please input your guess")
   reader := bufio.NewReader(os.Stdin)
​
   for {
      //读取一行输入
      input, err := reader.ReadString('\n')
      if err != nil {
         fmt.Println("读取错误,请再试一次~")
         return
      }
      //去掉换行符
      input = strings.TrimSuffix(input, "\n")
      input = strings.TrimSuffix(input, "\r") //不知为何,我这里会多读取一个\r,去除之后就能得到单纯的数字字符串
​
      //转换为数字
      guess, err := strconv.Atoi(input)
      if err != nil {
         fmt.Println("无效的输入,请输入整数值")
      }
      fmt.Println("You guess is", guess)
​
      //与随机值相比较
      if guess > secretNumber {
         fmt.Println("猜大了")
      } else if guess < secretNumber {
         fmt.Println("猜小了")
      } else {
         fmt.Println("恭喜你,猜对了!!")
         break
      }
   }
}

2.在线词典

命令行调用后添加参数,根据这个参数调用第三方api去查询到这个单词的音标、注释

彩云翻译

使用彩云翻译翻译单词

image-20220507222336999.png

curl 'https://api.interpreter.caiyunai.com/v1/dict' -X POST -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:100.0) Gecko/20100101 Firefox/100.0' -H 'Accept: application/json, text/plain, */*' -H 'Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2' -H 'Accept-Encoding: gzip, deflate, br' -H 'Content-Type: application/json;charset=utf-8' -H 'X-Authorization: token:qgemv4jr1y38jyq6vhvi' -H 'app-name: xy' -H 'version: 1.8.0' -H 'os-type: web' -H 'os-version: ' -H 'device-id: ' -H 'Origin: https://fanyi.caiyunapp.com' -H 'Connection: keep-alive' -H 'Referer: https://fanyi.caiyunapp.com/' -H 'Sec-Fetch-Dest: empty' -H 'Sec-Fetch-Mode: cors' -H 'Sec-Fetch-Site: cross-site' -H 'Pragma: no-cache' -H 'Cache-Control: no-cache' --data-raw '{"trans_type":"en2zh","source":"good"}'

另一个网站

不止为何,我生成的代码执行会乱码,所以生成的代码就不贴了

func main() {
   client := &http.Client{}
   //与视频讲解的不同,此处视频里这个代码需要被注释掉才能使用
   //var data = strings.NewReader(`{"trans_type":"en2zh","source":"good"}`)
   request := DictRequest{TransType:"en2zh",Source:"good"}
   //返回request的 JSON 编码,返回为byte数组
   buf,err := json.Marshal(request)
   if err != nil {
      log.Fatal(err)
   }
   //将byte数组转化为流
   var data = bytes.NewBuffer(buf)
   //返回一个Request请求
   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)
}
fmt.Printf("%s\n", bodyText)

json字符串转go结构体

这边贴过去的代码可能浏览器会报错,这可能是因为粘贴过去有换行,根据我自身的操作,将换行删除后可以生成以下代码

type AutoGenerated 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"`
}

然后再添加上以下代码,就可以将上面请求返回的byte[]转化成结构体对象

var dictRequest AutoGenerated
//将bodyText中的值写入到dictRequest中
err = json.Unmarshal(bodyText,&dictRequest)
if err != nil {
   log.Fatal(err)
}
fmt.Printf("%#v\n",dictRequest)

处理响应,筛选得到想要的

//判断状态码是否为200,也就是这个请求是否正确
if resp.StatusCode != 200 {
   //如果错误打印出来状态码和响应报文
   log.Fatal("StatusCode : ",resp.StatusCode,"body : ",string(bodyText))
}
var dictResponse AutoGenerated
//将bodyText中的值写入到dictRequest中
err = json.Unmarshal(bodyText,&dictResponse)
if err != nil {
   log.Fatal(err)
}
fmt.Printf("%#v\n", dictResponse)
​
//筛选想得到的内容
fmt.Println("good","UK:", dictResponse.Dictionary.Prons.En,"US:",dictResponse.Dictionary.Prons.EnUs)
for _,item := range dictResponse.Dictionary.Explanations{
   fmt.Println(item)
}

修改原先代码,将原本的func main() 变成一个函数 func query(word string)

request := DictRequest{TransType:"en2zh",Source:"good"} 中固定值good改成函数传入值word

添加一个新的程序入口

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)
}

执行结果