走进 Go 语言基础语法
目录
1. Go 语言的 Hello World
2. 变量
3. if else
4. 循环
5. switch
6. 数组
7. 切片
8. map
9. range
10. 函数
11. 指针
12. 结构体
13. 结构体方法
14. 错误处理
15. 字符串操作
16. 字符串格式化
17. JSON处理
18. 时间处理
19. 数字解析
20. 进程信息
1. Go 语言的 Hello World
package mainimport "fmt"func main() {fmt.Println("hello world")
}
代码解读:
-
这是 Go 语言程序的包声明。每个 Go 程序都必须属于一个包,package main
:main
包是一个特殊的包,它定义了一个可独立执行的程序。 -
导入了import "fmt"
:fmt
包,fmt
包提供了格式化输入输出的函数。在这个程序中,我们将使用fmt.Println
函数来输出内容。 -
这是程序的入口点,每个可独立执行的 Go 程序都必须有一个func main()
:main
函数。main
函数不接受任何参数,也不返回任何值。 -
调用fmt.Println("hello world")
:fmt
包中的Println
函数,该函数用于在控制台打印输出内容,并在输出后自动添加一个换行符。这里输出的内容是 “hello world” 字符串。
2. 变量
变量声明:
在go语言中,最基本的方式是使用var
关键字:
package mainimport "fmt"func main() {// Go 语言会对声明的变量进行初始化var a int //整数类型的零值是 0var b string //字符串类型的零值是""var c bool //布尔类型的零值是falsefmt.Println(a, b, c)
}
也可以同时声明多个变量或在声明变量时指定初始值:
func main() {//同时声明多个变量var d, e intfmt.Println(d, e) // 0 0//在声明变量时指定初始值var f int = 10fmt.Println(f) //10
}
GO语言中使用var关键字声明变量,支持自动推导变量的类型:[ 重点 ]
func main() {var a = "字符串"var b = 10var c = truefmt.Println(a, b, c) //字符串 10 true
}
变量的简短声明:
Go 语言还支持简短声明变量,使用 := 操作符。例如:
func main() {a := 10b, c := true, "字符串"fmt.Println(a, b, c) //10 true 字符串
}
a := 10
,这种方式声明并初始化了变量a,它会自动根据赋值的类型来推断变量的类型。这里a被推断为整数类型。但是简短声明只能在函数内部使用。
同样可以使用简短声明来同时声明多个变量。
变量的类型转换:
func main() {a := 10b := float64(a) //将a转为浮点数赋给bfmt.Println(b)
}
常量的定义:
使用const
关键字来定义常量:
const name = "小林"func main() {fmt.Println(name)
}
如果一组常量的类型相同,还可以省略类型声明。例如:const A, B = 1, 2
Go 语言会自动根据赋值来推断常量的类型,这里A
和B
都被推断为整数类型。
3. if else
go语言中 if 语句后没有括号,若将条件判断语句写在括号中,运行代码时编译器会自动去掉括号:
func main() {if 8%4 == 0 {fmt.Println("8 is divisible by 4")}
}
Go语言中 if 后必须跟{ },不能像C/C++ 一样与 if 语句写在同一行。
4. 循环
Go语言中没有while循环和do-while循环,只有for循环一种。且for循环后面跟的条件同样也是不带括号的。
func main() {//for后没有条件 为死循环for {fmt.Println("loop")break}//打印0~9 (写法1)for j := 0; j < 10; j++ {fmt.Println(j)}//打印0~9 (写法2)i := 0for i <= 9 {fmt.Println(i)i = i + 1}
}
5. switch
同样,switch后变量名不加括号:
func main() {a := 2switch a {case 1:fmt.Println("one")case 2:fmt.Println("two") //输出twocase 3:fmt.Println("three")case 4, 5:fmt.Println("four or five")default:fmt.Println("other")}
}
6. 数组
数组的声明:
func main() {//通过var声明var a [5]inta[2] = 2fmt.Println(a) //[0 0 2 0 0]//通过len获取数组长度fmt.Println(len(a)) // 5//简短声明b := [5]int{1, 2, 3}fmt.Println(b) //[1 2 3 0 0]
}
在实际开发过程中其实很少使用到数组,因为它的长度是固定的 ,更多时候是使用切片。
7. 切片
切片它是一个可变长度的数组,切片有三个重要的属性:指针、长度和容量。
使用make
函数创建:
func main() {//创建一个长度为3的字符切片。s := make([]string, 3)s[0] = "a"s[1] = "b"s[2] = "c"fmt.Println("get:", s[2]) // cfmt.Println("len:", len(s)) // 3
}
操作方法
追加元素:
使用append
函数可以向切片中追加一个或多个元素,如果切片的容量不足,Go 会自动创建一个新的底层数组,并将原切片的内容复制到新数组中。
func main() {//创建一个长度为3的字符切片。s := make([]string, 3)s[0] = "a"s[1] = "b"s[2] = "c"//追加元素s = append(s, "d", "e", "f")fmt.Println(s) // [a b c d e f]
}
复制切片:
使用copy
函数可以将一个切片的内容复制到另一个切片中。
func main() {//创建一个长度为3的字符切片。s := make([]string, 3)s[0] = "a"s[1] = "b"s[2] = "c"//追加元素s = append(s, "d", "e", "f")fmt.Println(s) // [a b c d e f]//复制切片c := make([]string, len(s))copy(c, s) //将s切片的内容复制到c切片中fmt.Println(c) // [a b c d e f]
}
切片的切片:
可以从一个切片中再创建一个子切片,子切片与原切片共享底层数组。
func main() {//创建一个长度为6的字符切片。s := make([]string, 6)s[0] = "a"s[1] = "b"s[2] = "c"s[3] = "d"s[4] = "e"s[5] = "f"//切片截取操作fmt.Println(s[2:5]) // [c d e]fmt.Println(s[:5]) // [a b c d e]fmt.Println(s[2:]) // [c d e f]
}
8. map
在 Go 语言中,map
是一种无序的键值对集合。它的主要作用是可以根据键(key
)快速地查找对应的值(value
)。map
的键是唯一的,在同一个map
中不能有两个相同的键。
创建方式1:通过make函数
func main() {m := make(map[string]int)m["one"] = 1m["two"] = 2fmt.Println(m) // map[one:1 two:2]fmt.Println(len(m)) // 2fmt.Println(m["one"]) // 1fmt.Println(m["unknow"]) // 0
}
创建方式1:使用字面量创建
m2 := map[string]int{"one": 1, "two": 2}var m3 = map[string]int{"one": 1, "two": 2}fmt.Println(m2, m3) // map[one:1 two:2] map[one:1 two:2]
删除键值对:
func main() {m := make(map[string]int)m["one"] = 1m["two"] = 2m["three"] = 3fmt.Println(m) // map[one:1 three:3 two:2]//删除键值对delete(m, "one")fmt.Println(m) // map[three:3 two:2]
}
特别注意:Go中的map是完全无序的,遍历的时候不会按字母顺序或插入顺序输出
9. range
在 Go 语言中,range
是一个用于遍历各种数据结构(如切片、数组、字符串、映射map
)的关键字。它提供了一种简洁的方式来遍历这些数据结构中的元素。
func main() {m := make(map[string]int)m["one"] = 1m["two"] = 2m["three"] = 3//使用range遍历mapfor k, v := range m {fmt.Println(k, v)}
}
10. 函数
func add(a int, b int) int {return a + b
}
/*
func add(a, b int) int {return a + b
}
*/
func main() {res := add(1, 2)fmt.Println(res) // 3
}
实际的业务逻辑代码中,几乎所有的函数都返回多个值,第一个值是真正的返回结果,第二个值是错误信息。例如:
func exists(m map[string]string, k string) (v string, ok bool) {v, ok = m[k]return v, ok
}
该函数返回第一个值v,也就是真正的value,返回第二个值ok,也就是判断是否存在
11. 指针
相比于C/C++里的指针,golang中支持的操作非常有限,这里的主要用途就是对传入的参数进行修改,
//无效
func add2(n int) {n += 2
}
//有效
func add2ptr(n *int) {*n += 2
}func main() {n := 5add2(n)fmt.Println(n) // 5add2ptr(&n)fmt.Println(n) // 7
}
- 在 add2 函数中,接受了一个整数参数
n
,并将其值加 2。但是由于 Go 语言是值传递,n += 2
只是修改了函数内部局部变量n
的值,不会影响到函数外部调用处的变量。所以这个函数对传入的参数的修改不会影响到原始变量。所以在main
函数中调用add2(n)
后,n
的值仍然是 5 。 - 在 add2ptr 函数中,接受一个整数指针参数
n
,通过解引用指针修改其所指向的值,将其值加 2。在main
函数中调用add2ptr(&n)
后,由于是通过指针修改了原始变量的值,所以n
的值变为 7。
在 Go 语言中,函数参数可以是值传递或指针传递。值传递会创建参数的副本,对副本的修改不会影响到原始值;而指针传递允许函数直接修改调用者的变量,因为它们共享相同的内存地址。
12. 结构体
在 Go 语言中,结构体 (struct
) 是一种用户自定义的数据类型,用于将多个不同类型的字段组合在一起,以表示一个复杂的数据结构。
type student struct {name stringage intgender string}
结构体实例化
1. 基本实例化
func main() {type student struct {name stringage intgender string}s := student{"小谷", 20, "男"} // {小谷 20 男}fmt.Println(s)
}
2. 指定字段初始化
func main() {type student struct {name stringage intgender string}s := student{name: "小谷", gender: "男"}fmt.Println(s) // {小谷 0 男}
}
没有被指定初始化的字段,会被自动初始化为其类型的零值。
3. 使用new函数实例化 (有点Java的味道了)
package mainimport "fmt"func main() {type student struct {name stringage intgender string}//使用new函数实例化s := new(student)s.name = "小谷"s.age = 20s.gender = "男"fmt.Println(s.name, s.age, s.gender) // 小谷 20 男
}
new
函数用于创建一个指定类型的指针。对于结构体,它会返回一个指向结构体的指针。- 这里
new(student)
创建了一个student
结构体的指针,然后通过指针访问其字段并赋值。
结构体的比较
如果结构体的所有字段都是可比较的,那么这个结构体也是可比较的:
type Point struct {X intY int
}func main() {p1 := Point{1, 2}p2 := Point{1, 2}if p1 == p2 {fmt.Println("p1 and p2 are equal") // p1 and p2 are equal} else {fmt.Println("p1 and p2 are not equal")}
}
如果结构体包含不可比较的字段(如
map
、slice
等引用类型),那么这个结构体是不可比较的。如果尝试比较这样的结构体,会导致编译错误。
13. 结构体方法
package mainimport "fmt"type user struct {name stringpassword 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
}
14. 错误处理
基础概念:
在 Go 语言中,错误处理是构建健壮程序的重要部分。Go 语言没有像其他一些语言那样的异常机制,而是使用返回值来表示错误。大部分可能出现错误的函数都会返回一个error
类型的值,这个error
类型是一个接口,定义如下:
type error interface {Error() string
}
这意味着任何实现了Error()
方法并返回一个字符串的类型都满足error
接口。当函数执行出现问题时,会返回一个非nil
的error
值来告知调用者错误信息。
简单的错误处理示例:
package mainimport ("fmt""os"
)func main() {file, err := os.Open("nonexistent_file.txt")if err!= nil {fmt.Println("Error opening file:", err)return}// 正常处理文件defer file.Close()
}
这里尝试打开一个不存在的文件,os.Open
函数会返回一个非nil
的err
值(在文件操作中,os.Open
函数用于打开一个文件,它返回一个文件指针和一个error
值),通过检查err!= nil
来判断是否出现错误。如果有错误,就打印错误信息并终止程序的执行;如果没有错误,就可以正常处理文件,并且使用defer
关键字确保文件最终会被关闭。
案例二:
package mainimport ("errors""fmt"
)type user struct {name stringpassword 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{{"wang", "2279"}}, "wang")if err != nil {fmt.Println(err)return}fmt.Println(u.name) // wangif u, err := findUser([]user{{"wang", "2279"}}, "zhang"); err != nil {fmt.Println(err) // not foundreturn} else {fmt.Println(u.name)}
}
15. 字符串操作
在标准库strings包中有许多字符串工具函数:
package mainimport ("fmt""strings"
)func main() {a := "hello"//判断字符串中是否包含另一个字符串fmt.Println(strings.Contains(a, "ll")) // true//字符计数fmt.Println(strings.Count(a, "l")) // 2//判断开头fmt.Println(strings.HasPrefix(a, "he")) // true//判断结尾fmt.Println(strings.HasSuffix(a, "llo")) // true//查找某个字符串的位置fmt.Println(strings.Index(a, "ll")) // 2//连接多个字符串fmt.Println(strings.Join([]string{"he", "llo"}, "-")) // he-llo//重复多个字符串fmt.Println(strings.Repeat(a, 2)) // hellohello//字符替换 ,-1表示进行无限制的替换 即替换所有匹配的字符fmt.Println(strings.Replace(a, "e", "E", -1)) // hEllo//分割fmt.Println(strings.Split("a-b-c", "-")) // [a b c]//转小写fmt.Println(strings.ToLower(a)) // hello//转大写fmt.Println(strings.ToUpper(a)) // HELLO//获取字符串长度fmt.Println(len(a)) // 5//对于中文,一个中文会对应多个字符b := "你好"fmt.Println(len(b)) // 6
}
16. 字符串格式化
在go语言的 fmt 包中有许多字符串格式化的方法,常用的有:
fmt.Sprintf函数:
在 Go 语言中,fmt.Sprintf
是最常用的字符串格式化函数之一。例如,将一个整数和一个字符串格式化为一个新的字符串:
package mainimport ("fmt"
)func main() {age := 20name := "WenHui"result := fmt.Sprintf("My name is %s and I am %d years old.", name, age)fmt.Println(result) // My name is WenHui and I am 20 years old.
}
这里%s
是用于格式化字符串的占位符,它会被name
变量的值替换;%d
是用于格式化整数的占位符,会被age
变量的值替换。 (和C语言一样)
格式化占位符的类型:
- 整数类型
%d
:用于格式化十进制整数。例如,fmt.Sprintf("%d", 10)
会返回"10"
。%b
:用于格式化二进制整数。如fmt.Sprintf("%b", 10)
会返回"1010"
。%o
:用于格式化八进制整数,fmt.Sprintf("%o", 10)
返回"12"
。%x
和%X
:用于格式化十六进制整数,%x
返回小写字母表示的十六进制数,%X
返回大写字母表示的十六进制数。例如,fmt.Sprintf("%x", 10)
返回"a"
,fmt.Sprintf("%X", 10)
返回"A"
。- 浮点数类型
%f
:用于格式化十进制浮点数。例如,fmt.Sprintf("%f", 3.14)
会返回"3.140000"
。可以通过指定精度来控制小数点后的位数,如fmt.Sprintf("%.2f", 3.14)
会返回"3.14"
。- 字符串类型
%s
:用于格式化字符串,如前面例子所示。%q
:用于将字符串用双引号引起来并进行转义。例如,fmt.Sprintf("%q", "hello")
返回""hello""
,如果字符串中有特殊字符,会进行相应的转义。- 布尔类型
%t
:用于格式化布尔值。例如,fmt.Sprintf("%t", true)
返回"true"
,- 指针类型
%p
:用于格式化指针的值,以十六进制形式输出。例如,对于一个指针变量p
,fmt.Sprintf("%p", p)
会返回指针的十六进制表示。
在golang中还可以用%v来打印任意类型的变量
type point struct {x, y int
}func main() {s := "hello"n := 123p := point{1, 2}fmt.Println(s, n) // hello 123fmt.Println(p) // {1 2}fmt.Printf("s=%v\n", s) // s=hellofmt.Printf("n=%v\n", n) // n=123fmt.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}
}
%v 只打印值
%+v 打印字段的名字和值 (对于结构体类型)
%#v 它比
%+v
更加详细,包含了结构体的包名(main
)、结构体类型名(Point
)以及字段名称和值
17. JSON处理
对于已有的结构体,保证其每个字段首字母大写,那么这个结构体就能用 json.Marshal 序列化,序列化后生成一个 buf数组(可以理解为一个字符串)
package mainimport ("encoding/json""fmt"
)type userInfo struct {Name stringAge int `json:"age"`Hobby []string
}func main() {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 userInfoerr = json.Unmarshal(buf, &b)if err != nil {panic(err)}fmt.Printf("%#v\n", b) // main.userInfo{Name:"wang", Age:18, Hobby:[]string{"Golang", "TypeScript"}}
}
18. 时间处理
在 Go 语言中,可以使用time.Now()
函数来获取当前的时间。这个函数返回一个time.Time
类型的值,它包含了年、月、日、时、分、秒、纳秒以及时区等信息。
时间戳的概念:时间戳是从 1970 年 1 月 1 日 UTC 到指定时间所经过的秒数。在 Go 语言中,可以使用Unix()
方法来获取时间戳。
package mainimport ("fmt""time"
)func main() {//获取当前时间now := time.Now()fmt.Println("当前时间:", now) // 2024-11-04 13:35:48.1742921 +0800 CST m=+0.000000001//时间格式化formattedTime := now.Format("2006-01-02 15:04:05")fmt.Println("格式化后的时间:", formattedTime) // 2024-11-04 13:35:48//获取时间戳timestamp := now.Unix() fmt.Println("当前时间戳:", timestamp) // 1730698548//从时间戳转换为时间timeFromTimestamp := time.Unix(timestamp, 0)fmt.Println("从时间戳转换后的时间:", timeFromTimestamp) //2024-11-04 13:35:48 +0800 CST//时间计算newTime := now.Add(24 * time.Hour)fmt.Println("新的时间:",newTime) //2024-11-05 13:35:48.1742921 +0800 CST m=+86400.000000001
}
19. 数字解析
package mainimport ("fmt""strconv"
)func main() {//将字符串转为整数n, _ := strconv.Atoi("521")fmt.Println(n) // 521//将字符串解析为浮点数(float64)f, _ := strconv.ParseFloat("3.14", 64)fmt.Println(f) // 3.14//将字符串 "1314" 按照十进制(基数为 10)解析为 int64 类型的整数n1, _ := strconv.ParseInt("1314", 10, 64)fmt.Println(n1) // 1314//整数转字符串str := strconv.Itoa(123)fmt.Println(str) //123
}
20. 进程信息
package mainimport ("fmt""os""os/exec"
)func main() {// go run example/20-env/main.go a b c dfmt.Println(os.Args) // [/var/folders/8p/n34xxfnx38dg8bv_x8l62t_m0000gn/T/go-build3406981276/b001/exe/main a b c d]fmt.Println(os.Getenv("PATH")) // /usr/local/go/bin...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(string(buf)) // 127.0.0.1 localhost
}
🌸🌸🌸 完结撒花 🌸🌸🌸
博主WX:g2279605572 欢迎大家与我交流!