Go 语言入门指南:基础语法和常用特性解析 | 豆包MarsCode AI刷题

49 阅读13分钟

安装Go语言
前往go.dev下载go

屏幕截图 2024-11-23 231622.png 安装VScode扩展
image.png

基础语法

  1. 包管理
    Go语言中的包是一个重要的概念,用于组织和管理代码。包可以看作是一组相关的源代码文件的集合。通过使用包,可以将代码模块化,使得代码更加清晰和易于维护。Go语言标准库中就包含了大量的包,例如fmt(用于格式化输入输出)、math(用于数学运算)等。
    其中包操作有包引入和包声明

    • 包声明:每个Go源文件都必须属于一个包。在每个源文件的第一行通常会有一个package声明,指定该文件属于哪个包。
    • 包引入:使用其他包中的函数、变量或类型时,需要先导入这些包。使用import关键字来导入包。 其中,包名可以使用相对路径或绝对路径
  2. 基本数据类型
    在Go语言中,变量和常量是用来存储数据的重要元素。它们提供了存储值的方法,并允许你在程序中引用这些值。

    • 变量:可以用var声明或:=隐式声明,可以等于整型、浮点型、字符串。可以在变量名后规定类型,也可以让Go语言推测变量类型,但注意这不像Js那样类型可变,一个变量定义了一个类型后不可变类型。可以像python一样一组变量声明一组初值。一个变量如果只声明不赋值默认为0,但必须表明变量类型。
    • 常量:用const声明不能隐式声明也不能多变量声明,另外可以用const()对集体声明,其余和变量声明方式一致。
    • 枚举:用iota表示,本质是常量,但在const()中赋值,第一个赋值iota,当前值为0,下面其他会依次变成1、2、3……递增
    • 匿名变量:用_表示,如果我们不关心返回值可以用这个
  3. 控制结构与range

    • 选择:
      • if 语句:if 语句用于执行基于条件判断的代码块。可以在 if 语句后面直接跟一个布尔表达式,也可以使用初始化语句。
      • switch 语句:switch 语句用于多分支选择,可以根据多个条件来决定执行哪一段代码。
    • 循环
      • for 语句:for 语句是Go语言中唯一的循环结构。它可以用于无限循环、条件循环和迭代循环。
      • 很遗憾,没有while
    • range
      range 语句用于遍历数组、切片、映射以及通道
      • 当你使用 range 遍历数组或切片时,它会返回两个值:索引和元素值。
      • 当你使用 range 遍历字符串时,它会将字符串视为一系列的字节,range 会返回每个字节及其位置。
      • 当你使用 range 遍历映射时,它会返回两个值:键和对应的值。
      • 当你使用 range 遍历通道时,它会不断地从通道中读取数据,直到通道关闭为止。
    • 跳转语句:break和continue
  4. 函数

    • 定义:基本函数用func定义,格式 func 函数名(参数列表) (返回值列表) {代码块}
    • 多返回值:在Go语言中,函数不仅可以返回单个值,还可以返回多个值。通常,多返回值用于返回一个主要的结果和一个或多个辅助信息(例如错误信息)。多返回值通过逗号分隔的列表来表示。返回值列表可以不规定参数名,但需要返回相关变量。如果都有变量名,规定的返回变量可以在函数中参与运算,同时可以只写一个return结束函数,也可以不return那几个变量,只return同类型变量。
    • 匿名函数:没有函数名的函数,可以被定义后立即执行,可以被var和隐式赋值赋给别的变量,亦可以被当作参数传给别的函数做参数(注:上三种操作,除了第一种,剩下两种普通函数也可以这么干)
    • 匿名函数支持闭包操作,即函数返回一个被加工过的匿名函数
  5. 数组与切片
    在Go语言中,数组和切片是非常重要的数据结构,它们用于存储一组相同类型的元素。虽然它们的功能相似,但在使用上有着显著的区别。

    • 数组:数组是一组具有固定大小的相同类型元素的集合。一旦创建,数组的长度是固定的,不能改变。用arr := [5]int{1, 2, 3, 4, 5}形式定义,其中[5]可用[...]让编译器自己算{}里元素的大小,{}也不是要一定带着
    • 切片:除了[]里不要写长度,其他和数组相似,有append追加和[:]切片以及sort包里其他的方法,可以用make()创建一个切片。也可以通过delete删除
  6. 映射
    在Go语言中,映射(map)是一种无序的键值对集合。映射提供了一种快速查找数据的方式,其中键(key)是唯一的,而值(value)可以重复。映射是动态的数据结构,可以在运行时改变大小。 可以形如 m := map[string]int{"apple": 5,"banana": 10,"orange": 7,}定义也可以形如 m := make(map[string]int) \n m["apple"] = 5 \n m["banana"] = 10 \n m["orange"] = 7 \n 定义

可以用make定义,用delete删除

注意,他没有并发安全,如果要并发请加sync.Mutex

  1. 结构体

    • 结构体可以通过type关键字和struct关键字定义。如:type Person struct

    • 可以使用结构体字面量或new关键字创建结构体实例。如:p1 := Person{Name: "Alice", Age: 30} 也可以 p2 := new(Person) \n p2.Name = "Bob" \n p2.Age = 25

    • 匿名结构体允许你在不预先定义的情况下直接定义结构体类型。如p := struct { Name string Age int }{ Name: "David", Age: 40, }

    • 可以在结构体上定义方法,这些方法可以接收结构体的实例作为接收者。形如func (绑定的结构体) 函数名() 返回值,如果func (结构体指针) 函数命() 返回值 就会改变原结构体里的值

  2. 指针
    在Go语言中,指针是一种特殊的变量类型,它存储的是另一个变量的内存地址,而不是实际的值。使用指针可以避免不必要的内存复制,提高程序的效率。下面详细介绍Go语言中指针的使用方法。

    • 声明指针:声明一个指针变量需要使用*符号,并指定该指针指向的数据类型。如var p *int
    • 使用&操作符获取变量的内存地址。
    • 使用*操作符对指针进行解引用,以访问或修改指针所指向的值。
    • 指针的零值为nil,表示该指针没有指向任何有效的内存地址。
    • 指针也可以作为数组的元素。
    • 传递指针给函数可以避免复制大的数据结构,从而提高性能。
  3. 错误处理
    Go语言中的错误通常使用error接口来表示。这个接口非常简单,只有单一的方法Error() string。任何实现了这个方法的类型都可以作为错误处理。
    fmt.Errorf和errors.New:这两个函数可以用来创建新的错误实例,其中fmt.Errorf允许你使用格式化字符串。

  4. JSON
    Go语言提供了内置的支持来处理JSON数据,这主要通过 encoding/json 包来实现。这个包包含了一系列的功能,从序列化(把Go对象转换成JSON字符串)到反序列化(把JSON字符串转换成Go对象)。

    • 将Go对象序列化为JSON字符串:要将Go对象转换为JSON字符串,你可以使用 json.Marshal 或 json.MarshalIndent 函数。Marshal 函数简单地将Go对象转换为紧凑的JSON字符串,而 MarshalIndent 则允许你在生成的JSON字符串中添加缩进以便更易读。
    • 将JSON字符串反序列化为Go对象:要将JSON字符串转换为Go对象,你可以使用json.Unmarshal函数。
  5. 时间
    在Go语言中,时间操作主要通过标准库中的time包来完成。这个包提供了丰富的功能,包括时间的解析、格式化、计算以及各种时间相关的操作。下面将详细介绍这些功能。

    • 获取当前时间:要获取当前的时间,可以使用time.Now()函数。该函数返回一个Time对象,包含了当前的日期和时间信息。
    • 时间格式化:Time对象可以通过Format方法进行格式化输出。这个方法接受一个字符串作为格式化模板,类似于C语言中的strftime函数。
    • 解析时间字符串:要从字符串中解析时间,可以使用Parse方法。这个方法接受两个参数:一个是时间格式模板,另一个是要解析的时间字符串。
    • 时间计算:Time对象支持基本的算术运算,比如加减秒数、分钟数、小时数等。
    • 时间比较:Time对象可以直接使用比较运算符(如<, >, ==等)进行比较。
    • 定时器:定时器是time包提供的另一种重要工具,用于在指定的时间间隔后执行某个操作。
    • 周期性任务:如果需要周期性地执行某些任务,可以使用ticker。ticker类似于定时器,但是会每隔固定的时间间隔重复触发。

12.进程
os.Args, os.Getenv, os.Setenv, 和 exec.Command 是Go语言中用于处理命令行参数、环境变量以及执行外部命令的重要工具。下面分别介绍它们的使用方法:

    • os.Getenv 和 os.Setenv:os.Getenv 用于从环境中读取值,而 os.Setenv 用于设置环境变量。
    • exec.Command:exec.Command 用于创建一个新的进程来执行外部命令。它返回一个 Cmd 结构体,该结构体提供了控制进程执行的方法。
    • os.Args:os.Args是一个字符串切片,包含了程序运行时的所有命令行参数。第一个元素通常是程序本身的路径,后续元素则是传递给程序的参数。
  1. 处理数字:
    在Go语言中,处理数字字符转换为数字的常用函数包括strconv包中的Atoi用于将字符串转换为整数,以及ParseInt和ParseFloat用于更灵活地解析不同基数的整数和浮点数.
  2. 处理字符串:
    在Go语言中,处理字符串的主要函数和方法包括 len() 获取字符串长度,for ... range 遍历字符串中的每个字符(包括多字节字符),strings 包中的 Contains, Index, Replace, Split,Count,HasPrefix,HasSuffix,Join,Repeat,Replace,ToLower,ToUpper 等函数用于常见的字符串操作,如查找子串、替换和分割。此外,还有 fmt.Sprintf 和 fmt.Sprintf 的变体用于格式化字符串输出,而 strconv 包提供了将字符串与其他基本数据类型之间相互转换的功能。这些工具覆盖了大多数常见的字符串处理需求。

特性
Go语言以其简洁、易读和易写的语法、静态类型和编译型的特点、高效的并发处理通过轻量级的协程和通道机制、丰富的标准库、跨平台和可移植性、强大的性能以及支持多范式编程,并在编译阶段进行静态类型检查以确保类型安全,成为一种高效、可靠且易于学习的语言。

示例程序

  • 代码:
package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strconv"  
	"strings"
	"sync"
	"time"
)

// 自定义错误类型
type CustomError struct {
	Code    int
	Message string
}

func (e *CustomError) Error() string {
	return fmt.Sprintf("错误码: %d, 错误信息: %s", e.Code, e.Message)
}

// 常量和枚举示例
const (
	Sunday = iota
	Monday
	Tuesday
	Wednesday
	Thursday
	Friday
	Saturday
)

// 结构体定义
type Person struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

// 结构体方法
func (p *Person) Birthday() {
	p.Age++
}

// 返回多个值的函数,包含错误处理
func divide(a, b float64) (quotient float64, err error) {
	if b == 0 {
		return 0, &CustomError{
			Code:    1001,
			Message: "除数不能为0",
		}
	}
	quotient = a / b
	return
}

// 模拟可能出错的文件操作
func processFile(filename string) error {
	// 检查文件是否存在
	if _, err := os.Stat(filename); os.IsNotExist(err) {
		return fmt.Errorf("文件 %s 不存在: %w", filename, err)
	}
	return nil
}

// 闭包函数示例
func counter() func() int {
	count := 0
	return func() int {
		count++
		return count
	}
}

// 执行外部命令的函数
func executeCommand(command string, args ...string) error {
	cmd := exec.Command(command, args...)

	// 获取标准输出
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return fmt.Errorf("无法获取标准输出管道: %w", err)
	}

	// 开始执行命令
	if err := cmd.Start(); err != nil {
		return fmt.Errorf("命令启动失败: %w", err)
	}

	// 打印标准输出
	output := make([]byte, 1024)
	n, err := stdout.Read(output)
	if err != nil {
		return fmt.Errorf("读取标准输出失败: %w", err)
	}

	fmt.Printf("命令输出: %s\n", output[:n])

	// 等待命令完成
	if err := cmd.Wait(); err != nil {
		return fmt.Errorf("命令执行失败: %w", err)
	}

	return nil
}

func main() {
	// 1. 基础错误处理
	if result, err := divide(10, 0); err != nil {
		fmt.Println("除法错误:", err)
		if customErr, ok := err.(*CustomError); ok {
			fmt.Printf("这是一个自定义错误,错误码:%d\n", customErr.Code)
		}
	} else {
		fmt.Printf("除法结果: %f\n", result)
	}

	// 2. 文件操作错误处理
	err := processFile("nonexistent.txt")
	if err != nil {
		fmt.Println("文件处理错误:", err)
	}

	// 3. 进程和环境变量操作
	// 获取当前进程ID
	fmt.Printf("当前进程ID: %d\n", os.Getpid())

	// 获取当前工作目录
	currentDir, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("当前工作目录: %s\n", currentDir)

	// 设置和获取环境变量
	os.Setenv("MY_CUSTOM_VAR", "自定义环境变量值")
	fmt.Printf("环境变量MY_CUSTOM_VAR: %s\n", os.Getenv("MY_CUSTOM_VAR"))

	// 获取所有环境变量
	fmt.Println("\n环境变量列表:")
	for _, env := range os.Environ()[:3] { // 只显示前三个环境变量
		fmt.Println(env)
	}

	// 获取命令行参数
	fmt.Printf("\n命令行参数: %v\n", os.Args)

	// 执行外部命令
	fmt.Println("\n执行外部命令:")
	cmd := exec.Command("echo", "Hello, World!")
	output, err := cmd.Output()
	if err != nil {
		fmt.Println("命令执行错误:", err)
	} else {
		fmt.Printf("命令输出: %s\n", output)
	}

	// 4. 基本数据类型和变量
	var name string = "Alice" // 使用了这个变量
	age := 25
	pi := 3.14159
	fmt.Printf("\n基本数据类型变量: name = %s, age = %d, pi = %f\n", name, age, pi)

	// 5. 数组和切片
	numbers := [5]int{1, 2, 3, 4, 5}
	slice := numbers[1:4]
	slice = append(slice, 6)

	// 6. 映射
	var userAges sync.Map
	userAges.Store("Bob", 30)
	userAges.Store("Alice", 25)

	// 7. range遍历
	fmt.Println("\n使用range遍历切片:")
	for i, v := range slice {
		fmt.Printf("索引: %d, 值: %d\n", i, v)
	}

	// 8. JSON处理
	person := Person{Name: "Charlie", Age: 35}
	jsonData, err := json.MarshalIndent(person, "", "  ")
	if err != nil {
		fmt.Println("JSON编码错误:", err)
	} else {
		fmt.Printf("\nJSON数据:\n%s\n", string(jsonData))
	}

	// 9. 时间处理
	now := time.Now()
	fmt.Printf("\n当前时间: %s\n", now.Format("2006-01-02 15:04:05"))

	// 10. 字符串处理
	str := "Hello, World!"
	fmt.Printf("\n字符串操作:\n")
	fmt.Printf("长度: %d\n", len(str))
	fmt.Printf("包含'World': %v\n", strings.Contains(str, "World"))
	fmt.Printf("转大写: %s\n", strings.ToUpper(str))
	fmt.Printf("分割结果: %v\n", strings.Split(str, ", "))

	// 11. 数字处理
	numStr := "123"
	num, err := strconv.Atoi(numStr)
	if err != nil {
		fmt.Println("字符串转换为数字失败:", err)
	} else {
		fmt.Printf("\n字符串转换为数字: %d\n", num)
	}

	// 12. 错误处理链
	if err := executeCommand("nonexistent-command"); err != nil {
		fmt.Printf("\n命令执行错误: %v\n", err)
	}

	// 13. 使用errors包:自定义错误
	errExample := errors.New("这是一个由errors包创建的简单错误")
	fmt.Println("\n使用errors包创建的错误:", errExample)

	// 14. 使用path/filepath包
	filePath := "some/path/to/file.txt"
	absolutePath, err := filepath.Abs(filePath)
	if err != nil {
		fmt.Println("获取绝对路径失败:", err)
	} else {
		fmt.Printf("文件的绝对路径: %s\n", absolutePath)
	}

	// 15. 使用sort包
	unsorted := []int{5, 2, 8, 1, 4}
	sort.Ints(unsorted)
	fmt.Println("\n排序后的切片:", unsorted)

	// 16. 使用sort包对字符串进行排序
	names := []string{"Bob", "Alice", "Charlie"}
	sort.Strings(names)
	fmt.Println("\n排序后的名字:", names)
}
  • 运行结果:

image.png