Go语言基础语法
一、Go语言开发环境搭建
1.安装编译器,go.dev/
2.配置Go语言开发环境,这里用的是JetBrains的Goland
二、基础语法
2.1 - Hello World
package main //定义了包名,程序的入口文件
import (
"fmt" //导入了fmt包
)
func main(){
fmt.Println("hello world")
}
2.2 - 变量
Go 语言变量名由字母、数字、下划线组成,其中首个字符不能为数字。
声明变量的一般形式是使用 var 关键字:
var a = "initial"
var b,c int = 1,2
var d = true //根据值自行判定变量类型。
var e float64 //指定变量类型,如果没有初始化,则变量默认为零值。
f := float32(e) //简短形式,使用 := 赋值操作符
定义常量
const s string = "constant"
const h = 100
const i = 3e20 / h
iota,特殊常量,可以认为是一个可以被编译器修改的常量。
const (
a = iota
b = iota
c = iota
)
2.3 - 基础语法
1. if-else循环
if 布尔表达式 {
/* 在布尔表达式为 true 时执行 */
} else {
/* 在布尔表达式为 false 时执行 */
}
例如:
package main
import "fmt"
func main(){ //大括号不能单独一行
if 7%2==0{
fmt.Println("7 is even")
} else {
fmt.Println("7 is odd")
}
}
注意:和C语言不同,Go中大的左括号{不能单独一行
2. for循环
package main
import "fmt"
func main(){ //大括号不能单独一行
i := 1
for{
fmt.Println("loop")
break
}
for j:=7; j<9; j++{
fmt.Println(j)
}
for n:=0; n<5; n++{
if n%2==0{
continue
}
fmt.Println(n)
}
for i<=3{
fmt.Println(i)
i = i + 1
}
}
3. switch语句
语法格式:
switch var1 {
case val1:
...
case val2:
...
default:
...
}
变量 var1 可以是任何类型,而 val1 和 val2 则可以是同类型的任意值。类型不被局限于常量或整数,但必须是相同的类型;或者最终结果为相同类型的表达式。
和c/c++的不同点: 1.每一条case语句后不用加break,执行完后直接跳出switch语句
2.可以使用任意的变量类型,比如字符串、结构体等
package main
import (
"fmt"
"time"
)
func main(){ //大括号不能单独一行
t := time.Now()
switch {
case t.Hour()<12:
fmt.Println("It's before noon")
default:
fmt.Println("It's after noon")
}
}
4. 数组
Go 语言提供了数组类型的数据结构。
数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整型、字符串或者自定义类型。
相对于去声明 number0, number1, ..., number99 的变量,使用数组形式 numbers[0], numbers[1] ..., numbers[99] 更加方便且易于扩展。
数组元素可以通过索引(位置)来读取(或者修改),索引从 0 开始,第一个元素索引为 0,第二个索引为 1,以此类推。
声明数组:
/* var arrayName [size]dataType */
var numbers [5]int
初始化数组:
var numbers = [5]int{1, 2, 3, 4, 5}
//或者是
numbers := [5]int{1, 2, 3, 4, 5}
如果数组长度不确定,可以使用 ... 代替数组的长度,编译器会根据元素个数自行推断数组的长度:
var balance = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
//或
balance := [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
初始化数组中 {} 中的元素个数不能大于 [] 中的数字
在实际的业务代码中,我们很少直接使用数组,因为数组的长度基本上是固定的,我们更多的时候用的是切片。
5. 切片(Slice)
Go 语言切片是对数组的抽象。
Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
代码示例:
package main
import "fmt"
func main(){
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]
fmt.Println(s[:5]) //[a b c d e]
fmt.Println(s[2:]) //[c d e f]
good := []string{"g","o","o","d"}
fmt.Println(good) //[g o o d]
}
6. map
在其他语言中,可能叫做哈希或者是字典。
Map 是一种无序的键值对的集合。
Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。
Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,遍历 Map 时返回的键值对的顺序是不确定的。
在获取 Map 的值时,如果键不存在,返回该类型的零值,例如 int 类型的零值是 0,string 类型的零值是 ""。
Map 是引用类型,如果将一个 Map 传递给一个函数或赋值给另一个变量,它们都指向同一个底层数据结构,因此对 Map 的修改会影响到所有引用它的变量。
代码示例:
package main
import "fmt"
func main(){
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
r,ok := m["unknow"]
fmt.Println(r,ok) //0 false
delete(m, "one")
m2 := map[string]int{"one":1,"two":2}
var m3=map[string]int{"one":1,"two":2}
fmt.Println(m2,m3)
}
Golang 中map的元素是完全无序的,遍历的时候是随机输出的
7. range
对于一个slice或者是map等,我们可以通过range来进行快速遍历,过程更加简洁。在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对。
代码示例:
package main
import "fmt"
func main(){
nums := []int{2,3,4}
sum := 0
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
}
}
8. 函数
函数的定义格式如下:
func function_name( [parameter list] ) [return_types] {
函数体
}
- func:函数由 func 开始声明
- function_name:函数名称,参数列表和返回值类型构成了函数签名。
- parameter list:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
- return_types:返回类型,函数返回一列值。return_types 是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types 不是必须的。
- 函数体:函数定义的代码集合。
代码示例:
package main
import "fmt"
func add(a int, b int) int {
return a + b
}
func add2(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
}
func main() {
res := add(1, 2)
fmt.Println(res) // 3
v, ok := exists(map[string]string{"a": "A"}, "a")
fmt.Println(v, ok) // A True
}
9. 指针
一个指针变量指向了一个值的内存地址。
类似于变量和常量,在使用指针前你需要声明指针。指针声明格式如下:
var var_name *var-type
代码示例:
package main
import "fmt"
func add2(n int) {
n += 2 //浅拷贝,对n不起作用
}
func add2ptr(n *int) {
*n += 2
}
func main() {
n := 5
add2(n)
fmt.Println(n) // 5
add2ptr(&n)
fmt.Println(n) // 7
}
10. 结构体
Go 语言中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数据类型。
结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。
代码示例:
package main
import "fmt"
type user struct {
name string
password string
}
func main() {
a := user{name: "wang", password: "1024"}
b := user{"wang", "1024"}
c := user{name: "wang"}
c.password = "1024"
var d user
d.name = "wang"
d.password = "1024"
fmt.Println(a, b, c, d) // {wang 1024} {wang 1024} {wang 1024} {wang 1024}
fmt.Println(checkPassword(a, "haha")) // false
fmt.Println(checkPassword2(&a, "haha")) // false
}
func checkPassword(u user, password string) bool {
return u.password == password
}
func checkPassword2(u *user, password string) bool {
return u.password == password
}
11.结构体方法
Go语言中可以用结构体去定义一些方法,这非常类似于其它语言中的类函数,
代码示例:
package main
import "fmt"
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
}
12. 错误处理
Go 语言通过内置的错误接口提供了非常简单的错误处理机制。
我们可以在编码中通过实现 error 接口类型来生成错误信息。
函数通常在最后的返回值中返回错误信息。使用 errors.New 可返回一个错误信息
代码示例:
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{{"wang", "1024"}}, "wang")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(u.name) // wang
if u, err := findUser([]user{{"wang", "1024"}}, "li"); err != nil {
fmt.Println(err) // not found
return
} else {
fmt.Println(u.name)
}
}
13. 字符串操作
1、统计字符串的长度,按字节len(str)
2、字符串遍历,同时处理有中文的问题r := []rune(str)
3、字符串转整数:n, err := strconv.Atoi("12")
4、整数转字符串 str = strconv.ltoa(12345)
5、字符串转 []byte: var bytes= []byte("hello go")
6、[]byte转字符串: str = string([]byte{97, 98, 99})
7、10进制转2, 8, 16进制: str = strconv.Formatlnt(123, 2)//2->8, 16
8、查找子串是否在指定的字符串中: strings.Contains("seafood", "oo") //true
9、统计一个字符串有几个指定的子串: num := strings.Count("hello","l")
10、不区分大小写的字符串比较(==是区分字母大小写的): fmt.Println(strings.EqualFoldl"abc", "Abc")
11、返回子串在字符串第一次出现的index值,如果没有返回-1 :index := strings.Index("abcHello","ll")
12、返回子串往字符串最后一次出现的index, 如没有返回-1:index := strings.LastIndex("abcHelloll","ll")
13、将指定的子事替换成另外一-个子串: strings Replace("go go hll", "go". "go语言", n) n可以指定你希望替换几个,如果n=-1表示全部替换
14、按照指定的某个字符,为分割标识,将一个字符串拆分成字符串数组:
//常用字符串
package main
import (
"fmt"
"strings"
)
func main() {
strArr := strings.Split("hello,hello,hello",",")
for i := 0; i < len(strArr); i++ {
fmt.Println(i,strArr[i])
}
fmt.Println("分割的结果为:",strArr)
}
15、将字符串的字母进行大小写的转换:str := strings.ToLower("GOLANG") str1 := strings.ToUpper("golang")
16、将字符串左右两边的空格去掉 str := strings.TrimSpace(" hello,cat ")
17、将字符串左右两边指定的字符去掉: str := strings.Trim("?hello,cat?","?")
14. 字符串格式化
Go提供了几种打印格式,用来格式化一般的Go值,例如 下面的%v打印了一个point结构体的对象的值:
p := point{1, 2}
fmt.Printf("%v\n", p)
可以用 %v 来打印任意类型的变量
可以用 %+v 来打印更加详细的内容
package main
import "fmt"
type point struct {
x, y int
}
func main() {
s := "hello"
n := 123
p := point{1, 2}
fmt.Println(s, n) // hello 123
fmt.Println(p) // {1 2}
fmt.Printf("s=%v\n", s) // s=hello
fmt.Printf("n=%v\n", n) // n=123
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
}
15. JSON处理
JSON (JavaScript Object Notation)是一种比XML更轻量级的数据交换格式,在易于人们阅读和编写的同时,也易于程序解析和生成。尽管JSON是JavaScript的一个子集,但JSON采用完全独立于编程语言的文本格式,且表现为键/值对集合的文本描述形式(类似一些编程语言中的字典结构),这使它成为较为理想的、跨平台、跨语言的数据交换语言。
使用json.Marshal()函数可以对一组数据进行JSON格式的编码。
输出字段名的首字母都是大写的,如果你想用小写的首字母怎么办呢?把结构体的字段名改成首字母小写的?JSON输出的时候必须注意,只有导出的字段(首字母是大写)才会被输出,如果修改字段名,那么就会发现什么都不会输出,所以必须通过struct tag定义来实现。
代码示例:
package main
import (
"encoding/json"
"fmt"
)
type userInfo struct {
Name string
Age int `json:"age"` //把字段名称改为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 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"}}
}
16. 时间处理
时间包括时间值和时区, 没有包含时区信息的时间是不完整的、有歧义的. 和外界传递或解析时间数据时, 应当像HTTP协议或unix-timestamp那样, 使用没有时区歧义的格式, 如果使用某些没有包含时区的非标准的时间表示格式(如yyyy-mm-dd HH:MM:SS), 是有隐患的, 因为解析时会使用场景的默认设置, 如系统时区, 数据库默认时区可能引发事故. 确保服务器系统、数据库、应用程序使用统一的时区, 如果因为一些历史原因, 应用程序各自保持着不同时区, 那么编程时要小心检查代码, 知道时间数据在使用不同时区的程序之间交换时的行为.
代码示例:
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
fmt.Println(now) // 2022-03-27 18:04:59.433297 +0800 CST m=+0.000087933
t := time.Date(2022, 3, 27, 1, 25, 36, 0, time.UTC)
t2 := time.Date(2022, 3, 27, 2, 30, 36, 0, time.UTC)
fmt.Println(t) // 2022-03-27 01:25:36 +0000 UTC
fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute()) // 2022 March 27 1 25
fmt.Println(t.Format("2006-01-02 15:04:05")) // 2022-03-27 01:25:36
diff := t2.Sub(t)
fmt.Println(diff) // 1h5m0s
fmt.Println(diff.Minutes(), diff.Seconds()) // 65 3900
t3, err := time.Parse("2006-01-02 15:04:05", "2022-03-27 01:25:36")
if err != nil {
panic(err)
}
fmt.Println(t3 == t) // true
fmt.Println(now.Unix()) // 1648738080
}
17. 数字解析
代码示例:
package main
import (
"fmt"
"strconv"
)
func main() {
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
}
18. 进程信息
代码示例:
package main
import (
"fmt"
"os"
"os/exec"
)
func main() {
// go run example/20-env/main.go a b c d
fmt.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
}