这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记
Go语言上手-基础语言
1 Go语言的特点
- 高性能、高并发
- 语法简单、学习曲线平缓
- 丰富的标准库
- 完善的工具链
- 静态链接
- 快速编译
- 跨平台
- 垃圾回收
2 Go语言基础语法
变量
Go语言是强类型语言,变量需要显式声明;
变量声明方式一:
var 变量名 类型 = 值
var b,c int = 1,2
声明后没初始化的变量,会自动初始化为零值(0,"",false,nil)
变量声明方式二:
变量名 := 值
a := 20
省略了变量类型,通过 := 完成声明和赋值
注:只能用在函数体内,不能用于全局变量声明和赋值,且局部变量需要被使用
常量
常量是在程序运行时,不会被修改的量。
常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。
const b,c = 20,"abc"
可以省略类型
if else
if后面的条件不用加圆括号,但if语句的后面要接花括号
if 7%2 == 0 {
fmt.Println("7 is even")
} else {
fmt.Println("7 is odd")
}
条件语句之前也可以加声明语句,声明的变量可以在语句的所有条件分支中使用
if num := 9; num < 0 {
fmt.Println(num, "is negative")
} else if num < 10 {
fmt.Println(num, "has 1 digit")
} else {
fmt.Println(num, "has multiple digits")
}
Go中没有三目运算符
for循环
for是Go中唯一的循环结构。
- 单个循环条件(while)
for i <= 3 {
fmt.Println(i)
i = i + 1
}
- 初始/条件/后续
for j := 7; j <= 9; j++ {
fmt.Println(j)
}
- 不带条件的for循环等于 for true,遇到break或者return才会停
for {
fmt.Println("loop")
break
}
- 支持continue
switch循环
- switch后面的变量不用加小括号,也可以不带表达式,替代if else语句;
- case的表达式也可以不用常量;
- default也是可选的;
- 每个case后不用加break(区别于Java),不会继续跑其他case;
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("It's before noon")
default:
fmt.Println("It's after noon")
}
数组
数组声明
var a [5]int
b := [5]int{1, 2, 3, 4, 5}
fmt.Println
打印数组时,会按照 [v1 v2 v3 ...]
的格式打印
数组长度固定,开发中较少使用
切片slice
定义切片
var identifier []type //声明一个未指定大小的数组来定义切片
var slice1 []type = make([]type, len) //make函数来声明
slice1 := make([]type, len) // := 语法省略var
make([]T, length, capacity) // make时还可以指定容量
切片的函数
append函数:
向slice追加元素,可能返回一个新的slice
s := make([]string, 3)
s = append(s, "d")
s = append(s, "e", "f")
- 还可以
slice[low:high]
来切片,slice[:]
等同于复制; copy(slice1,slice2)
复制slice1到slice2;- slice也可以组成多维数据结构,内部的slice长度可以不一致;
- fmt.Println打印slice和数组类似;
map
创建一个空 map,需要使用内建函数 make
:make(map[key-type]val-type)
使用 name[key] = val
语法来设置键值对
m := make(map[string]int)
m["k1"] = 7
m["k2"] = 13
函数 len
可以返回一个 map 的键值对数量;
函数 delete
可以从一个 map 中移除键值对;
取值时用_, prs = map[key]
可以查询map中是否包含指定key(包含的话prs为true,否则为true,防止不存在key和key对应的value为0,都会输出0的歧义)
range
- range在数组和 slice 中提供对每项的索引和值的访问,不需要索引时可以用
_
来忽略
nums := []int{2, 3, 4}
sum := 0
for _, num := range nums {
sum += num
}
- range在map中迭代键值对,也可以只遍历map的键;
kvs := map[string]string{"a": "apple", "b": "banana"}
for k, v := range kvs {
fmt.Printf("%s -> %s\n", k, v)
} ```
for k := range kvs {
fmt.Println("key:", k)
}
函数
- 定义的返回值类型在函数名后面
- 可以有多返回值,用
_
灵活选择 - 可变参数,可以接收任意数量的同类型参数,
func sum(nums ...int) {
fmt.Print(nums, " ")
total := 0
for _, num := range nums {
total += num
}
fmt.Println(total)
}
func main() {
sum(1, 2)
sum(1, 2, 3)
nums := []int{1, 2, 3, 4}
sum(nums...)
}
指针
指针变量指向了一个值的内存地址;
指针声明:
var var_name *var-type = &variable
&变量
:表示变量的存储地址,即指针;*指针
:表示指针对应的值; 函数调用时,基本数据是值传递,不会修改实参,可以通过指针进行传参,来修改实参
结构体
结构体中我们可以为不同项定义不同的数据类型。
结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。
结构体的定义:
type Books struct {\
title string\
author string\
subject string\
book_id int\
}
访问结构体成员:
结构体.成员名
结构体方法:
func (变量名 结构体名) 方法名 (参数列表) 返回值类型 {}
结构体位置可以带指针(操作结构体),也可以不带
接口interface
定义接口:
type geometry interface {
area() float64
perim() float64
}
结构体如果实现了接口中的所有抽象方法,就属于实现了该接口。 实现了该接口后,就可以调用接口中的方法。
错误处理
函数的返回值类型里加error,如果error为nil,就说明没有返回错误;
字符串操作
标准库的strings
包
字符串格式化
JSON处理
- 结构体的属性名字段首字母大写,就可以被其他包访问(类似于public)
- 同样可以通过json.Marshal来序列化,会变成buf数组(可以理解为字符串,默认是16进制编码),string(buf)类型转化后就可以打印字符串;
- 字符串可以通过json.unMarshal来反序列化到一个空变量;
时间处理
time.Now()
time.Date()
构造带时区的时间
Format格式,常用"2006-01-02 15:04:05"
now.Unix()
可以获取时间戳
数字解析
字符串和数字的转换,都在strconv
包下
进程信息
os.Args可以获取进程在执行时的命令行参数
os.Getenv 获取环境变量
os.Setenv 设置环境变量
3 猜谜游戏(实战项目1)
package main
import (
"bufio"
"fmt"
"math/rand"
"os"
"strconv"
"strings"
"time"
)
func main() {
maxNum := 100
// 用时间戳随机初始化种子
rand.Seed(time.Now().UnixNano())
// 生成随机数(0-100)
secretNum := rand.Intn(maxNum)
// fmt.Println("The secret number is ", secretNum)
fmt.Println("Please input your guess")
// 通过os.Stdin来读,用bufio.NewReader来转换为只读的流
reader := bufio.NewReader(os.Stdin)
for {
// 用ReadString来读取一行输入
input, err := reader.ReadString('\n')
if err != nil {
fmt.Println("An error occurred while reading input. Please try again", err)
continue
}
// 去掉换行和回车
input = strings.TrimSuffix(input, "\r\n")
// 字符串转换为数字
guess, err := strconv.Atoi(input)
if err != nil {
fmt.Println("Invalid input. Please input an integer value")
continue
}
fmt.Println("Your guess is ", guess)
if guess > secretNum {
fmt.Println("Your guess is bigger. Please try again")
} else if guess < secretNum {
fmt.Println("Your guess is smaller. Please try again")
} else {
fmt.Println("Correct, you Legend")
break
}
}
}
4 在线词典(实战项目2)
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
)
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"`
}
type DictRequest struct {
Transtype string `json:"trans_type"`
Source string `json:"source"`
UserId string `json:"user_id"`
}
func query(word string) {
// 创建http的client
client := &http.Client{}
request := DictRequest{Transtype: "en2zh", Source: word}
buf, err := json.Marshal(request)
if err != nil {
log.Fatal(err)
}
// 创建http的post请求,data是一个流
var data = bytes.NewReader(buf)
req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
// 设置请求头
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.127 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)
}
// 访问的body是一个流,用defer来关闭流,函数结束后从下往上触发
defer resp.Body.Close()
// 用ioutil把流读到内存里,变成字节数组
bodyText, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
// 防止resp的状态不是200,比如说是404等,加入检验代码可以方便排查问题
if resp.StatusCode != 200 {
log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))
}
// 打印的是json字符串
// fmt.Printf("%s\n", bodyText)
var dictResponse DictResponse
err = json.Unmarshal(bodyText, &dictResponse)
if err != nil {
log.Fatal(err)
}
// 打印详细信息
// fmt.Printf("%#v\n", dictResponse)
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)
}