Go语言入门指南 | 青训营
一、代码结构
// 标识属于哪个包
package main
// 引入库文件
import (
"fmt"
)
// 代码主体
func main() {
...
}
二、基础语法
1、变量申明
变量声明有两种方式:
func main(){
var a = "hello"
var b, c int = 1, 2
f := float32(e)
}
如上述代码片段所示,go变量可以通过 var 关键词进行申明,我们可以在变量后声明该变量的类型,也可以选择让编译器自己识别。
我们还可以省略 var 关键字,直接通过 := 符号,来声明变量。
go和其他语言一样,也拥有常量类型,通过const字段声明:
const c = 123
// 还可以通过()将一组常量包含起来
const (
PI float64 = 3.1415926
MAX int = 1000
...
)
2、条件判断
go的条件判断语句和C/C++类似,唯一的区别是,if后面的条件语句不需要使用“()”。如:
if 7%2 == 0 {
fmt.Println("7 is even")
} else {
fmt.Println("7 is odd")
}
3、循环
go中尤其仅有for循环,示例如下:
// 没有任何条件判断的情况下,即为死循环
for {
fmt.Println("hello world!")
}
// 类似于C/C++中的for使用,只是没有括号
for i := 1; i < 10; i++ {
fmt.Println(i)
}
// 此处类似while的用法
for i <= 10 {
fmt.Println(i)
i = i + 1
}
在go的循环中,也保留了break,continue关键字的使用,用法和C/C++一样。
4、分支结构(switch)
switch a {
case 1:
fmt.Println("one")
case 2:
fmt.Println("two")
default:
fmt.Println("other")
}
go的switch拥有更加丰富的功能,甚至可以不写判断的变量,用来代替if-else。如:
switch {
case a < b:
fmt.Println("a < b")
...
default:
fmt.Println("...")
}
5、数组&切片
数组的定义方式:
var a [5]int
这样就定义了一个长度为5的int类型的数组a,其余的使用和C/C++类似。
但是由于数组长度固定,我们一般不太会使用它,我们会使用更加灵活的切片。
切片的使用比较复杂,这边只做一些基础的介绍:
// 使用make创建切片
s := make([]string, 3)
s[0] = "a" // 通过下标可以直接访问
s[1] = "b"
s[2] = "c"
// 通过append命令,追加元素
// 不过由于其底层实现方式的原因,append之后会获得一个新切片,通过如下方式,可以实现原切片元素的追加
s = append(s, "d")
c := make([]string, len(s))
copy(c, s) // 通过copy函数,将s的数据拷贝到c中
这里稍微拓展一下切片的底层知识,方便大家去理解。
切片其实是一种数据结构,它存储了三个元素:指针,长度,容量。
- 指针指向一个底层数组
- 长度是当前切片存储的数据的长度
- 容量是切片可以存储的最大长度
切片的另一种用法:
// 假设现在存在一个切片:s = [a, b, c, d, e, f]
fmt.Println(s[2:5]) // output: [c, d, e]. 不包括第五个
fmt.Println(s[:5]) // output: [a, b, c, d, e] 从开头开始
fmt.Println(s[2:]) // output: [c, d, e, f] 一直到结尾
6、map
其他语言中,可能叫做hash、字典。
在日常开发中使用很广泛。
// 方括号内的为key的类型,方括号后的int为value类型
m := make(map[string]int)
// map存储键值对
m["one"] = 1
// 获取map的值
value, ok := m["one"] // ok的值可以用来确定该key是否存在
// 删除map中的某个键值对
delete(m, "one")
map在索引的时候,是一种偏向随机索引的过程。
7、range
可以使用range快速遍历切片和map。代码更加简洁。
nums := []int{2,3,4}
// 对数组使用range时,会产生两个数据,第一个为索引,第二个为值
for i, num := range nums {
...
}
m := map[string]string{"a": "A", "b": "B"}
// 对map使用range时,返回两个值,一个是key,一个是value
for k, v := range m {
....
}
8、函数
go中函数的结构如下所示:
func 函数名(变量1 变量1类型, 变量2 变量2类型) 返回类型 {
函数主体
}
// 示例
func add(a int, b int) int {
return a + b
}
// go的函数可以返回多个值
func exists(m map[string]string, k string) (v string, ok bool) {
v, ok = m[k]
return v, ok
}
9、指针
相比C/C++的指针,go的指针功能有限,主要用于对函数的参数进行修改。
func add1(n int) {
n += 2
}
func add2(n *int) {
*n += 2
}
func main() {
n := 5
add1(n) // main中的n不会改变
add2(&n) // 穿的参数需要增加取地址符,该函数会修改main中的n
}
10、结构体
结构体是带类型的字段的集合
// 此处定义一个结构体
type user struct {
name string
password string
}
// 创建结构体的若干种方法
a := user{name: "wang", password: "111"}
b := user{"wang", "111"}
c := user{name: "wang"}
c.password = "111" // 通过.运算符操作结构体内元素
给结构体申明成员函数:
package main
import "fmt"
// 此处定义一个结构体
type user struct {
name string
password string
}
// 定义一个user的成员函数
func (u user) checkPassword(password string) bool {
return u.password == password
}
// 主函数
func main() {
a := user{"wang", "111"}
fmt. Println(a.checkPassword("222")) // false
}
11、错误处理
go中想要进行错误处理,需要导入“errors”包。
不同于java的异常捕获处理方式,go往往会在函数的返回值中增加一个error类型的值实现错误处理。代码如下:
package main
import {
"fmt"
"errors"
}
type user struct {
name string
password string
}
func findUsers(users [user], name string) (v *user, err error) {
for _, u := range users {
if u.name == name {
return &u, nil // nil相当于java中的null
}
}
return nil, errors.New("not found")
}
func main() {
u, err := findUser([]user{{"wang", "111"}, {"zhang", "222"}}, "wang")
if err != nil {
... // 错误处理
}
}
12、字符串操作
字符串的常用操作
a := "hello"
strings.Contains(a, "ll") // 判断a中是否存在“ll”,此处发牛true
string.Count(a, "l") // 计算a中有多少个l,此处返回2
string.HasPrefix(a, "he") // 判断he是否为a的前缀,此处返回true
string.HasSuffix(a, "llo") // 判断llo是否为a的后缀,此处返回true
string.Index(a, "ll") // 计算ll在字符串中的位置,此处为2
string,Join([]string{"he", "llo"}, "-") // he-llo
string.Repeat(a, 2) // hellohello
string.Replace(a, "e", "E", -1) // hEllo
string.Split("a-b-c", "-") // [a b c]
string.ToLower(a) // hello
string.ToUpper(a) // HELLO
len(a) // 5
字符串的格式化,类似其他语言
import "fmt"
fmt.Println(a, b, c...) // 打印多个变量
fmt.Printf(....) // 格式化打印
// %v 万能格式
// %+v 更加详细
// %#v 更更加详细
// %d 整型数字
// %s 字符串
// ...
13、JSON操作
需要引入包“encoding/json”
go可以对结构体形式的内容进行序列化,不过要求结构体对应的属性必须首字母大写。
package main
import (
"fmt"
"encoding/json"
)
type userInfo struct {
Name string
Age int 'json:"age"' // 此处的tag是修改json之后的字段名称
Hobby []string
}
func main() {
a := userInfo{"gege", 18, []string{"sing", "jump", "rap"}}
buf, err := json.Marshal(a) // 通过json.Marshall函数实现序列化
fmt.Println(string(buf)) // {"Name":'gege', "age":18, "Hobby":["sing", "jump", "rap"]}
// 反序列化
var b userInfo
err = json.Unmarshal(buf, &b)
}
14、时间处理
需要导入包“time”
now := time.Now
// 获取某个详细字段
now.Year()
now.Month()
now.Day()
now.Hour()
now.Minute()
// 格式化
now.Format("2006-01-02 15:04:05") // 不同于其他语言的YYYY MM……这边使用的是一个固定的时间,改时间也写在了官方文档上的
// 时间解析
t, err := time.Parse("2006-01-02 15:04:05","2023-07-01 01-02-03")
// 获取时间戳
now.Unix()
15、字符串和数字的转换
需要导入包“strconv”
f, _ := strconv.ParseFloag("1.234", 64) // f = 1.234
// 10表示是10进制,如果传0,就自动推测;64表示64位
n, _ := strconv.ParseInt("111", 10, 64) // n = 111;
n2, _ := strconv.Atoi("123") // n2 = 123
s, _ := strconv.Itoa(123) // s = "123"
16、进程信息
需要导入包 “os”,“os/exec”
fmt.Println(os.Args) // 获取进程的参数信息
fmt.Println(os.Getenv("PATH")) // 获取环境变量
fmt.Println(os.Setenv("AA","BB")) // 设置环境变量
exec.Command("grep", "127.0.0.1", "/etc/hosts").CombinedOutput() // 通过此命令快速的启动子进程,并获取其输入输出