Go语言基础语法(主要是记录与C/C++的区别) | 青训营

24 阅读6分钟

Go语言基础

重点语法以及与C/C++区别

Hello World

package main // 表示入口文件

import (
    "fmt" // 标准输入输出库
)

func main() {
    fmt.Println("hello world") // 输出函数
}

变量类型

  • string, int, bool, float64, float32 等,优先级与 C/C++ 类似
  • 字符串是内置类型,可以直接用 + 拼接

变量声明

var a = "strings"  // 可以自动推导类型
var b int = 1  // 可以指定变量类型
c := b  // 也可以通过这种方式声明变量

常量

const d string = "constant"  // const 关键字声明常量

条件语句

if condition { 
    // 和 C/C++ 类似,不过条件处没有括号
    // 必须后接大括号,不能写在一行
} else {
}

if num := 9; num < 0 {
    // 可以先声明变量,再进行判断
}

循环语句

// 只有for循环
for {
    // for循环没有条件,表示死循环
}

for i := 1; i < 10; i++ {
    // 条件仍然没有小括号
}

选择语句

switch a {
    // 条件不需要括号
    case 1:
        // 默认会break,不用显式写break
    case 2, 3:
        // 多条件用逗号隔开
    default:
        // default一致
}

t := 1
switch {
    case t * 2 < 20:
        // case可以是任意的变量或结构体甚至是语句
        // 可以取代if-else语句,结构更清晰
}

数组

// 访问和修改与C/C++类似
// len函数求长度
var a [5]int
b := [5]int{1, 2, 3, 4, 5}
var c [2][3]int
fmt.Println(c) // 可以直接打印,会打印元素

切片

// 可变长度,可于声明时指定
s := make([]string, 3)
// append函数添加元素,必须将结果赋值回原数组
// append的时候容量不足会扩容并返回新的切片
s = append(s, "d", "e")
new_s := make([]string, len(s))
// 拷贝切片
copy(new_s, s)
// 切片操作
s[2:5] // 取出[2,5)的元素,注意左闭右开
s[:5] // 不支持负数索引
s[2:]
fmt.Println(s) // 可以直接打印,会打印元素

map

// Go中map完全无序
m := make(map[key_type]value_type)
m2 := map[string]int{"one": 1, "two": 2}
var m3 = map[string]int{"one": 1, "two": 2}
m["one"] = 1
delete(m, "one") // 删除key-value对
v, ok := m["unknow"] // 读取时加一个ok变量可以获得是否存在这个key-value对的信息
fmt.Println(v, ok) // 0 false 如果不存在k-v对,则ok为false

range

// range可以快速遍历,返回索引和值
nums := []int{2, 3, 4}
for index, num := range nums {
    // 如果不需要下标,可以用 _ 占位代替
}
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
}

函数

// 形参列表变量类型后置,返回值类型后置
func add(a int, b int) int {
}
// 形参列表变量类型一致时可以省略写在最后
func add2(a, b int) int {
}
// 可以返回多个返回值,一般是真实返回值以及状态变量的形式
func exists(m map[string]string, k string) (v string, ok bool) {
    v, ok = m[k]
    return v, ok
}

指针

  • 与C/C++类似,不过*是写在前面,如func add(n *int)
  • 取值时用*取值,如*n += 2
  • 传参时仍然用&取地址

结构体

  • 结构体的定义与使用
    • 语法与C语言类似,不过声明变量时先写变量名再写结构体的名字
    • 可以使用:=直接在声明时初始化结构体变量
    • 结构体可以作为函数的参数,也能传入地址,但是指针的访问不是->,还是.
type user struct {
    name string
    pswd string
}
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}
func checkPassword2(u *user, password string) bool {
    return u.password == password
}
  • 结构体方法
    • 结构体方法是在函数名前面加上(结构体变量名 结构体名)
    • 结构体方法的结构体变量名也能传入指针
    • 直接通过结构体变量名.结构体方法名使用结构体方法
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
}

错误处理

  • 错误处理需要引入errors模块
  • 返回值增加一个error类型的错误变量
    • 没有错误时error变量返回nil
    • 有错误时error变量返回错误信息errors.New("错误信息")
  • 可以直接fmt.Println()打印error类型的变量
package main

import (
    "errors" // 需要引入errors模块
    "fmt"
)

type user struct {
    name     string
    password string
}

func findUser(users []user, name string) (v *user, err error) { // 返回值增加一个err类型的错误变量
    for _, u := range users {
            if u.name == name {
                    return &u, nil // 没有错误err变量返回nil
            }
    }
    return nil, errors.New("not found") // 有错误err变量返回错误信息
}

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

字符串及格式化

  • 字符串基本操作函数
    • 需要引入strings模块
    • 使用strings.方法名
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))         字符串替换,第四个变量是要替换的次数,-1 将字符串 s 中的所有的 old 替换成 new// 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))                                   len函数计算字符串长度,中文字符长度不为1// 5
  • 字符串格式化
    • 使用fmt模块格式化打印字符串
    • fmt.Println()可以直接打印变量
    • fmt.Printf()类似于C语言的打印函数,不过%v可以表示所有类型的变量
      • %+v比较详细的打印变量的内容
      • %#v更加详细的打印变量的结构和内容
      • 此外,也能够用%.2f等格式
type point struct {
    x, y int
}

p := point{1, 2}
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}

Json文件处理

  • 使用encoding/json模块
  • 定义结构体时变量名首字母需要大写
  • 定义结构体时使用 json:"name" 标签指定在 JSON 编码和解码时使用的字段名称
  • json.Marshal()将数据结构序列化成json字符串
  • func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) 将数据结构转换为 JSON 字符串,并进行美化格式化输出,通过json.MarshalIndent()调用,参数 v 是要转换为 JSON 字符串的数据结构,prefix 是每行前面添加的前缀字符串,indent 是用于缩进的字符串
  • 序列化后的json字符串使用string()转换打印
  • 序列化后的json字符串通过json.Unmarshal()反序列化到一个空的变量中
package main

import (
	"encoding/json"
	"fmt"
)

type userInfo struct {
	Name  string
	Age   int `json:"age"` // 变量名首字母需要大写,使用 json:"name" 标签指定在 JSON 编码和解码时使用的字段名称
	Hobby []string
}

func main() {
	a := userInfo{Name: "wang", Age: 18, Hobby: []string{"Golang", "TypeScript"}}
	buf, err := json.Marshal(a) // 函数有返回错误信息
	if err != nil {
		panic(err)
	}
	// 序列化信息是十六进制保存,需要通过string转换为字符串
        fmt.Println(buf)         // [123 34 78 97...]
	fmt.Println(string(buf)) // {"Name":"wang","age":18,"Hobby":["Golang","TypeScript"]}

	buf, err = json.MarshalIndent(a, "", "\t") // 序列化json并美化格式
	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()方法
    • 时间变量的.Format()方法可以指定格式格式化时间变量
    • 时间变量的.sub()方法可以对时间变量和另一个时间变量作差
    • 时间变量的.Minutes(), .Seconds()方法可以将时间变量转换为以分钟或秒数为单位
    • 通过time.Parse()将指定的字符串解析为时间,2006-01-02 15:04:05是time模块标准样板,同时该函数会返回错误信息
    • 通过now.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
}

数字解析

  • 需要引入strconv模块
    • strconv.ParseFloat将字符串解析为浮点数,第二个参数是精度
    • strconv.ParseInt将字符串解析为整数,第二个参数是进制,0表示自动推断,第三个参数是精度
    • strconv.Atoi将字符串转为数字,strconv.itoA将数字转为字符串,如果字符串不合法会返回相应的错误信息
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

进程信息

  • 需要引入osos/exec模块
    • os.Args获取命令行参数,第一个值是当前程序二进制的位置
    • os.Getenv()获取环境变量
    • os.Setenv()设置环境变量
    • exec.Command()创建了一个命令对象,该命令对象表示要执行的命令,调用 CombinedOutput 方法执行命令并返回命令的标准输出和错误输出合并后的结果
// 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