Go基本语法
基础知识
-
package`表示该文件属于哪个包
-
import表示该文件导入哪些包 -
变量类型
- 使用
var定义时可以不声明数据类型,go会自动识别,如var a = "initial" - 使用
var时可以显式声明,如var b, c int = 1, 2 - 字符串是内置类型,可以直接通过
+拼接,如g := a + "foo" - 使用
const声明常量,go可以通过上下文自动识别数据类型,如const s string = "123"
- 使用
流程控制
-
if-elseif条件判断后不加括号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") } -
for(同if-else,条件判断不需要加括号)- 条件判断什么都不写表示死循环
for后条件中,初始化、条件判断、自增值这三部分每一部分都可以省略,中间用;隔开
for { //全部省略 fmt.Println("loop") break } for j := 7; j < 9; j++ { //全写 fmt.Println(j) } for i <= 3 { //部分省略 fmt.Println(i) i = i + 1 } -
switch(同if-else,条件判断不需要括号)- 在
GoLang中,不需要在每一条分支下加break,执行完对应分支下语句后会自动跳出 switch后也可以不加任何条件判断,在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 - 定义时自动初始化
- 可以直接通过
fmt.Print来打印,结果显示为[xxxxx]
var a [5]int a[4] = 100 fmt.Println("get:", a[2]) fmt.Println("len:", len(a)) b := [5]int{1, 2, 3, 4, 5} fmt.Println(b) var twoD [2][3]int for i := 0; i < 2; i++ { for j := 0; j < 3; j++ { twoD[i][j] = i + j } } fmt.Println("2d: ", twoD) - 定义时在数据类型前声明数组大小,如
-
slice切片 (可变长数组)- 通过
make来定义切片,第一个参数表示数据类型,第二个参数表示初始切片大小,如s := make([]string, 3) - 切片通过
append来添加元素,需要注意的是,添加结果必须赋值回去,因为切片的底层本质是存储了长度+容量+指向数组的指针,添加时如果发生扩容会产生一个新的指针,因此需要把新的指针返回回去。如s = s.append(s, "d") - 可以用
copy(target, source)来把一个切片赋值给另一个切片 - Go中的切片可以实现Python中的切片操作,如
fmt.Println(s[2:5])表示打印切片s中下标从2到5 (不包括5) 的元素
s := make([]string, 3) s[0] = "a" s[1] = "b" s[2] = "c" fmt.Println("get:", s[2]) // c fmt.Println("len:", len(s)) // 3 s = append(s, "d") s = append(s, "e", "f") fmt.Println(s) // [a b c d e f] c := make([]string, len(s)) copy(c, s) fmt.Println(c) // [a b c d e f] fmt.Println(s[2:5]) // [c d e] - 通过
-
Map (哈希、字典、键值对)
- 通过
make(Map[key]value)来建立Map,key表示键的数据类型,value表示值的数据类型,如m := make(Map[string]int)建立一个string-int的键值对 - 直接通过赋值来向
map中添加元素,如m["one"] = 1向m中添加一个"one":1的键值对 - 获取
map中的值时,可以设置两个参数,第一个参数表示返回值,第二个参数表示map中是否有该键,如r, ok = map["unknown"],若没有该键,返回值默认为0 - 通过
delete(map, key)来对Map中的键值对进行删除
m := make(map[string]int) m["one"] = 1 m["two"] = 2 fmt.Println(m) // map[one:1 two:2] fmt.Println(len(m)) // 2 fmt.Println(m["one"]) // 1 fmt.Println(m["unknow"]) // 0 r, ok := m["unknow"] fmt.Println(r, ok) // 0 false delete(m, "one") - 通过
基础语法
-
range遍历- 可以通过
range快速遍历slice和map - 格式如
for i, num := range nums,其中当nums为slice切片时,i表示索引,num表示对应位置的值;当nums为map键值对时,i表示key,num表示value
for i, num := range nums { sum += num if num == 2 { fmt.Println("index:", i, "num:", num) // index: 0 num: 2 } } fmt.Println(sum) // 9 m := map[string]string{"a": "A", "b": "B"} for k, v := range m { fmt.Println(k, v) // b 8; a A } for k := range m { fmt.Println("key", k) // key a; key b } - 可以通过
-
function函数- 参数的变量类型后置,即先声明变量名,再声明变量类型,如
func add(a int, b int) - 多数函数返回不止一个值,第一个值为真正的返回结果,第二个值通常为错误信息,一般为
bool类型 - 不指定返回值啧默认为空 (void)
func add(a int, 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 } - 参数的变量类型后置,即先声明变量名,再声明变量类型,如
-
point指针- 主要作用为对传入的参数进行修改
- 指针的*放在数据类型前
func add2ptr(n *int) { *n += 2 } -
struct结构体- 通过
type name struct{}定义结构体 - 定义结构体变量时没有指定的字段会默认置空
- 通过
name.field来获取/修改结构体对应字段的值
type user struct { name string password string } a := user{name: "wang", password: "1024"} b := user{"wang", "1024"} c := user{name: "wang"} c.password = "1024" - 通过
-
struct-method结构体方法- 可以为结构体定义对应的方法,类似其它语言的成员函数
- 通过
name.func来调用对应的结构体方法
type user struct { name string password string } 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来实现类似Java中的try-catch效果,error可以通过errors.New("xxxx")来定义 - 调用目标函数后先判断
error是否存在,若不存在才能继续执行相关业务逻辑
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") } - 一般函数通过定义第二个返回值
-
string常用字符串操作strings.Contains(s, "xx"),判断字符串中是否包含xxstrings.Count(s, "xx"),求字符串中xx的出现次数strings.HasPrefix(s, "xx"),判断字符串是否以xx打头strings.Index(s, "xx"),查找xx字符串的位置strings.Join([]string{a, b}, "-"),用-连接多个字符串strings.Repeat(s, int),将字符串重复数遍后返回strings.Replace(s, "a", "b", c),用b替换a,重复c次strings.Split(s, "-"),按-分割字符串strings.ToLower(s),将字符串转换为小写strings.ToUpper(s),将字符串转换为大写len(s):返回字符串长度,一个中文汉字可能对应多个字符
-
fmt字符串格式化- 通过
fmt.Println()打印对应变量并换行 - 通过
fmt.Printf("%v", s)格式化打印任意类型的变量,类似C中的printf - 通过
fmt.Printf("%+v", s)和fmt.Printf("%#v", s)来打印变量详细信息 - 通过
fmt.Printf("%.2f", s)打印保留两位小数的浮点数
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- 序列化前提:结构体的每个字段名首字母为大写,若不为大写则序列化结果不包含对应字段
- 序列化操作:通过
buf, err = json.Marshal(s)来对结构体进行序列化,打印buf时需要将其转换为string类型,输出string类型时为键值对的形式 - 若要输出常见
json格式,需要通过buf, err = json.MarshalIndent(a, "", "\t")实现,结果存放在buf中,a为原始数据 - 反序列化:通过
err = json.Unmarshal(buf, &b)来对序列化结果buf进行反序列化,将结果存放在b中,返回err
a := userInfo{Name: "wang", Age: 18, Hobby: []string{"Golang", "TypeScript"}} buf, err := json.Marshal(a) if err != nil { panic(err) } 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 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"}} -
time时间处理- 通过
time.Now()获取当前时间 - 通过
time.Date(Year, Month, Day, Hour, Minute, Second, Offset, time.UTC)来构造一个指定的时间,其中Offset表示距离标准时区的偏移 - 可以通过
t.Year(), t.Month()....获取对应时间信息 - 通过
diff = time1.sub(time2)可以获得一个时间差,可以通过diff.Minute(), diff.Second()获取对应时间信息 - 通过
time1.format("2006-01-02 15:04:05")来格式化一个时间信息,该字符串为固定格式 - 通过
time.Parse("2006-01-02 15:04:05", s)来将一个字符串格式化为时间信息 - 通过
time.Unix()获取时间信息对应的时间戳
- 通过
-
strconv数字解析- 需要先导入
strconv包才能实现字符串解析为数字 - 通过
strconv.ParseFloat(s, int)来讲指定字符串s解析为指定精度的浮点数 - 通过
strconv.ParseInt(s, int1, int2)来讲指定字符串s解析为指定进制int1指定精度int2的政整形 - 通过
strconv.Atoi(s),Go将自动识别转换对应字符串
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 - 需要先导入
-
env进程信息- 先导入
"os", "os/exec"包 - 通过
os.Args获取当前进程信息 - 通过
os.Getenv()和os.Setenv()来获取/设置环境变量
- 先导入
实现一个简单的数字炸弹游戏
package main
import (
"bufio"
"fmt"
"math/rand"
"os"
"strconv"
"strings"
"time"
)
func main() {
maxNum := 100
rand.Seed(time.Now().UnixNano()) //根据时间戳初始化随机种子
secretNumber := rand.Intn(maxNum) //根据随机种子生成随机数
fmt.Println("Please input your guess") //提示用户输入
reader := bufio.NewReader(os.Stdin) //重定义输入流
for {
input, err := reader.ReadString('\n') //读取用户输入
if err != nil {
fmt.Println("An error occured while reading input. Please try again", err)
continue
}
input = strings.Trim(input, "\r\n") //将最后输入的回车去除
guess, err := strconv.Atoi(input) //将输入转换为数字
if err != nil {
fmt.Println("Invalid input. Please enter an integer value")
continue
}
fmt.Println("You guess is", guess) //输出用户的输入
if guess > secretNumber { //进行判断
fmt.Println("Your guess is bigger than the secret number. Please try again")
} else if guess < secretNumber {
fmt.Println("Your guess is smaller than the secret number. Please try again")
} else {
fmt.Println("Correct, you Legend!")
break
}
}
}