青训营X豆包MarsCode 技术训练营第一课 | Go语言基础语法

112 阅读7分钟

Go基础语法

内容整理自字节内部课--走进 Go 语言基础语法

1.基本框架--HelloWorld

package--import--func

 package main
 ​
 import (
     "fmt"
 )
 ​
 func main() {
     fmt.Println("hello world")
 }
 ​

2.基本变量和声明

字符串、整型、浮点型、布尔型

  • 字符串为内置类型,可直接通过加号拼接,以及"<"">"比较

     g := a + "foo"
    
  • 变量声明:主要有两种

    1. var 变量名 = 值(自动推导/显式指定)

    2. 变量名 :=

           var a = "initial"
       ​
           var b, c int = 1, 2
       ​
           var d = true
       ​
           var e float64
       ​
           f := float32(e)
       ​
           g := a + "foo"
      
  • 常量声明:将 var 改为 const(无指定类型,自动指定类型)

         const s string = "constant"
     ​
         const h = 500000000
     ​
         const i = 3e20 / h
    
  • nil 是一个预定义的标识符,用于表示指针、接口、切片、映射、通道和函数类型的零值。对于指针类型,nil 表示该指针没有指向任何内存地址;对于接口类型,nil 表示该接口没有实现任何方法;对于切片、映射、通道和函数类型,nil 表示它们没有被初始化。

3.基本语句

  • if-else

    与C++类似,但 if 后无括号,且循环体必须有括号

         if 7%2 == 0 {
             fmt.Println("7 is even")
         } else {
             fmt.Println("7 is odd")
         }
    
  • for 循环

    同上,且 continue 与 break 同样适用

         for n := 0; n < 5; n++ {
             if n%2 == 0 {
                 continue
             }
             fmt.Println(n)
         }
    
  • switch 分支结构

    同上,但默认不需要每个分支加入 break,

         switch a {
         case 1:
             fmt.Println("one")
         case 2:
             fmt.Println("two")
         case 3:
             fmt.Println("three")
         case 4, 5:
             fmt.Println("four or five")
         default:
             fmt.Println("other")
         }
    

    go语言中的switch支撑功能更强大,可使用任意变量类型,还可以使用 if 类型语句

         t := time.Now()
         switch {
         case t.Hour() < 12:
             fmt.Println("It's before noon")
         default:
             fmt.Println("It's after noon")
         }
    

4. 其他数据结构

  • 数组

    与其他语言类似,重点在于声明语句

    	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)
    
  • 由于数组的长度固定,一般使用切片

    • 使用make创建切片

      s := make([]string, 3)
      
      good := []string{"g", "o", "o", "d"}
      
    • 可以直接用索引直接赋值

      	s := make([]string, 3)
      	s[0] = "a"
      	s[1] = "b"
      	s[2] = "c"
      
    • 使用append追加元素,注意用法:需要将原切片作为参数,执行方法后赋值回原切片

      s = append(s, "d")
      
    • 支持类似python的切片操作,但不支持负数索引

        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]
      
    • 可以用copy() 方法复制切片

      	c := make([]string, len(s))
      	copy(c, s)
      
  • map

    • 使用make() 创建map,或直接创建
    	m := make(map[string]int)
    	m["one"] = 1
    	m["two"] = 2
    
    	m2 := map[string]int{"one": 1, "two": 2}
    	var m3 = map[string]int{"one": 1, "two": 2}
    
    • 使用delete() 删除键值对

      	delete(m, "one")
      
    • 直接用 [] 进行查询,取值等操作

      	fmt.Println(len(m))      // 2
      	fmt.Println(m["one"])    // 1
      	fmt.Println(m["unknow"]) // 0
      
      	r, ok := m["unknow"]
      
    • map的遍历是近似随机的

  • range

    • 对于数组,返回值第一个位置为索引(可用_代替),第二个位置为对应值

      	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
      		}
      	}
      
    • 对于map,返回值第一个为key,第二个为value

      	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
      	}
      

5. 函数

  • 注意golang中,返回值类型后置

    func add(a int, b int) int {
    	return a + b
    }
    
  • golang支持返回多值,因此在一般业务逻辑中,一般返回两个值,第一个为运算结果,第二个为执行情况(报错信息)

    func exists(m map[string]string, k string) (v string, ok bool) {
    	v, ok = m[k]
    	return v, ok
    }
    

6. 指针

  • 与C语言类似(注意传入和处理时添加的特殊符号 *,&)

    func add2(n int) {
    	n += 2
    }
    
    func add2ptr(n *int) {
    	*n += 2
    }
    
    func main() {
    	n := 5
    	add2(n)
    	fmt.Println(n) // 5
    	add2ptr(&n)
    	fmt.Println(n) // 7
    }
    

7. 结构体

  • 与C语言类似,使用type关键字声明

    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
    }
    
  • 结构体方法(类似Java中的类成员函数),可通过结构体对象自身访问内部元素或修改内部元素(需要用到指针)

    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
    }
    

8. 错误处理

  • 需要导入errors包

  • 在定义方法时,加入一个error类型返回值,error对象通过errors.New("error Message") 创建

    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)
    	}
    }
    

9.字符串操作

  • 需要导入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----重复字符串
    	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----一个中文可能对应多个字符
    }
    
  • TrimSuffix(‘xxx’)去掉后缀

  • 字符串格式化

    • Printf()(可以使用%占位表示任意类型变量)

    • +v#v分别表示更详细和进一步详细

      	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
      
  • 字符串和数字转换

    • 需导入strconv

    • 操作如下

      	f, _ := strconv.ParseFloat("1.234", 64)
      	fmt.Println(f) // 1.234
      	//10表示进制,64表示精度
      	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
      

10.json操作

  • 需要导入encoding/json

  • 通过json.Marshal() 方法类序列化一个结构体对象,结果为一个十六进制编码数组,需要经过String类型转换才能正确输出

    	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"]}
    
  • 序列化后的结果可以通过Unmarshal() 方法,反序列化到一个空变量中

    	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"}}
    
  • 可以在结构体的变量定义中加上tag(如下),即可使得序列化后打印的字符串中字段名为对应tag

    type userInfo struct {
    	Name  string
    	Age   int `json:"age"`
    	Hobby []string
    }
    

11. 事件处理

  • 需要导入time

  • 具体操作如下:可进行的操作包括:

    • 使用time.Now()获取时间
    • 对时间格式化(注意特定执行格式)
    • 使用x.Year/Month/Day/Hour/Minute获取对应信息
    • 使用x.Sub()方法获取时间差,该时间差可以通过 Minute()和Seconds()方法获得相差的分数和秒数
    • 使用Parse()方法解析时间(将时间字符串转为time对象)
    • 通过x.Unix()获取时间戳
    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
    }
    

12.进程信息

  • 如下,知识有限,解读略

    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
    }