Go语言基础知识标题 | 豆包MarsCode AI刷题

109 阅读17分钟

什么是GO语言

  1. 高性能、高并发
  2. 语法简单、学习曲线平缓
  3. 丰富的标准库
  4. 完善的工具链
  5. 静态链接
  6. 快速编译
  7. 跨平台
  8. 垃圾回收

基础知识

Hello World

package main

import (
	"fmt"
)

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

var

package main

import (
	"fmt"
	"math"
)

func main() {
	var a = "initial"
	var b, c int = 1, 2
	var d = true
	var e float64
	f := float32(e)
	g := a + "foo"
	fmt.Println(a, b, c, d, e, f)
	fmt.Println(g)
	const s string = "hello"
	const h = 5200000
	const i = 3e20 / h
	fmt.Println(s, h, i, math.Sin(h), math.Sin(i))
}

var关键字声明

var a int = 10

如果在声明时进行初始化,可以省略类型:

var b = 20

:=短变量同时声明和初始化变量

c := 30

Go 是强类型语言,每个变量都有一个明确的类型,常见类型包括 int, float64, string, bool 等。

零值:

  • 如果变量被声明但未初始化,Go 会为其赋予“零值”:
    • 整数类型的零值是 0
    • 浮点类型的零值是 0.0
    • 布尔类型的零值是 false
    • 字符串的零值是 ""(空字符串)

作用域:

  • 变量的作用域是根据其声明的位置决定的,函数内部的变量只能在该函数内访问,而全局变量可以在整个包内访问。

const

常量使用 const 关键字声明,不能被修改:

const Pi = 3.14

常量可以是字符串、布尔值或数字:

const (
    A = "Hello"
    B = 42
    C = true
)

常量可以不指定类型,Go 会根据上下文推断类型:

const D = 5.0 //类型为float64

常量可以参与计算,编译时计算结果可以用作其他常量的初始化:

onst E = 10
const F = E * 2  // F 的值为 20

if-else

条件表达式可以是任何返回布尔值的表达式,如比较运算符 (==, !=, <, >, <=, >=) 和逻辑运算符 (&&, ||, !)。

短声明

if x := compute(); x > 0 {
    // x 大于 0 的处理
} else {
    // x 小于或等于 0 的处理
}
package main

import "fmt"

func main() {
	if 7%2 == 0 {
		fmt.Println("7 is even")
	} else {
		fmt.Println("7 is odd")
	}
	if 8%4 == 0 {
		fmt.Println("8 is divisible by 4")
	}
	if num := 9; num < 0 {
		fmt.Println(num, "is negative")
	} else if num < 10 {
		fmt.Println(num, "has 1 digit")
	} else {
		fmt.Println(num, "has multiple digits")
	}
}

for

只有for循环

基础的for循环

for i:=0;i<10;i++{
    fmt.Println(i)
}

省略初始化和现有条件

i := 0
for i < 10 {
    fmt.Println(i)
    i++
}

无限循环

for {
    // 永远执行的代码
}

迭代集合

fruits := []string{"apple", "banana", "cherry"}
for i, fruit := range fruits {
    fmt.Printf("%d: %s\n", i, fruit)
}

switch

**自动break:**Go的switch语句在匹配成功会自动退出,不需要显示使用break

表达式省略:switch后的表达式可以省略,省略表示true,可以用来代替多个if-else的条件判断

func main() {
    value := 10
    switch {
    case value < 5:
        fmt.Println("Less than 5")
    case value >= 5 && value < 10:
        fmt.Println("Between 5 and 10")
    default:
        fmt.Println("10 or more")
    }
}

类型判断:switch语句还可以用于类型判断,这在接口类型的断言中非常有用。

func main() {
    var x interface{} = 10

    switch i := x.(type) {
    case int:
        fmt.Printf("x is an int: %d\n", i)
    case string:
        fmt.Printf("x is a string: %s\n", i)
    default:
        fmt.Println("Unknown type")
    }
}

interface{}是Go语言中的一个特殊类型,表示空接口。它可以持有任何类型的值,因为所有类型都实现了空接口。这使得interface{}可以用作通用容器,能够存储任何类型的数据。

array

数组的定义: 包括元素类型和数组长度

var arr [5]int

初始化数组:

//直接初始化
arr := [5]int{1,2,3,4,5} 
//使用 ... 自动推导长度
arr2 := [...]int{10,20,30}

数组遍历:

arr := [5]rune{'a', 'b', 'c', 'd', 'e'}
for i, v := range arr {
    fmt.Println(i, v)
}
// 0 97
// 1 98
// 2 99
// 3 100
// 4 101
arr := [5]string{"a", "b", "c", "d", "e"}
for i, v := range arr {
    fmt.Println(i, v)
}
// 0 a
// 1 b
// 2 c
// 3 d
// 4 e

数组的值传递: 在Go语言中,数组是值类型,传递数组时会复制整个数组

func modifyArray(arr [5]int) {
	arr[0] = 100
}

func main() {
	original := [5]int{1, 2, 3, 4, 5}
	modifyArray(original)
	fmt.Println(original[0]) // 仍然是 1,因为传递的是副本
}

切片: 切片是Go中对数组的更灵活的封装,允许动态大小。

slice := []int{1,2,3}
slice = append(slice,4)

slice

定义切片: 切片的定义不需要指定长度,使用[]符号表示。

var slice []int

创建切片: 切片可以通过字面量、内置的make函数或者通过数组切片创建

// 字面量创建
slice := []int{1,2,3,4,5}
// make函数创建
slice := make([]int,5) //创建一个长度5的切片,元素初始化为0
slice := make([]int,0) //创建一个空切片

**切片的长度和容量:**长度是切片元素的数量,容量是切片底层数组的总大小。

length := len(slice)
capacity := cap(slice)

动态扩展切片: 使用append函数向切片添加元素

slice = append(slice,6)

切片是引用类型: 切片本质是对底层数组的引用,多个切片可以共享一个底层数组。

slice1 := []int{1, 2, 3}
slice2 := slice1
slice2[0] = 100
fmt.Println(slice1)

map

定义和创建Map: 可以使用内置的make函数或者字面量来创建map

// 使用map函数
m := make(map[string]int) //创建一个键为字符串类型、值为整型的map
// 使用字面量
m := map[string]int{
    "apple":2,
    "banana":2,
}

添加更新元素: 赋值语法向map中添加或更新键值对

m["orange"] = 3       // 添加新的键值对
m["apple"] = 5        // 更新键 "apple" 的值
value := m["banana"] // 访问键 "banana" 对应的值

删除元素: 使用deletemap中删除指定的键

delete(m, "banana") // 删除键 "banana" 及其对应的值

检查键是否存在: 通过多重赋值可以检查一个键是否存在

value,exists := m["apple"]
if exists{
    
}else{
    
}

遍历Map: 使用for循环和range关键字遍历map中的键值对

for k, v := range m {
    fmt.Println(k, v)
}

空map: 使用nil初始化的map无法添加元素

var emptyMap map[string]int // nil map
emptyMap = make(map[string]int) // 正确的初始化

map是引用类型: 多个变量可以引用同一个map

m1 := make(map[string]int)
m1["key"] = 1
m2 := m1
m2["key"] = 2
fmt.Println(m1["key"])

并发安全: Go的map不是线程安全的,多个goroutine同时读写同一个map可能会导致程序崩溃。在并发环境中,可以使用sync.Mutexsync.Map来实现安全访问。

range

range是一个关键字,用于在for循环中遍历数组、切片、字符串、映射和通道。

用于数组和切片: 返回每个元素的索引和值

arr := []int{1, 2, 3, 4, 5}
for index, value := range arr {
    fmt.Printf("Index: %d, Value: %d\n", index, value)
}

用于字符串: 返回字符串的索引和 Unicode码点值

str := "hello"
for index, char := range str {
    fmt.Printf("Index: %d, Char: %c\n", index, char)
}

用于映射: 键和值

m := map[string]int{"a": 1, "b": 2, "c": 3}
for key, value := range m {
    fmt.Printf("Key: %s, Value: %d\n", key, value)
}

用于通道: 当用于通道时,range可以接收从通道中发送的值,直到通道被关闭。

ch := make(chan int)
go func() {
    for i := 0; i < 5; i++ {
        ch <- i
    }
    close(ch) // 关闭通道
}()

for value := range ch {
    fmt.Println(value) // 输出通道中的值
}

func

函数的定义:

func add(a int,b int) int{
    return a+b
}

多个返回值: go语言支持多个返回值

func swap(a,b int)(int,int){ //a的类型需要是int
    return b,a
}
x,y := swap(1,2)

命名返回值: 方便在函数内部直接修改返回值

func divide(a,b int)(res int,err error){
    if b == {
        err = fmt.Errorf("division by zero")
        return
    }
    res = a/b
    return
}

可变参数函数: 使用...声明可变参数,使函数能够接受任意数量的参数。

func sum(numbers ...int)int{
    total := 0
    for _,number := range numbers{
        total += number
    }
    return total
}
res := sum(1,2,3,4,5)

闭包: Go中的函数可以嵌套,内部函数可以访问外部函数变量,这称为闭包。

func counter() func() int{
    count := 0
    return func() int {
        count++
        return count
    }
}
nextCount := counter()
fmt.Println(nextCount)	//输出1
fmt.Println(nextCount)	//输出2

高阶函数: 函数可以作为参数传递给其他函数,也可以作为返回值返回,这称为高阶函数。

// fn函数名
func apply (fn func(int) int,value int) int{
    return fn(value) 
}
// double函数
double := func(x int) int{
    return x*2
}
res := apply(double,5)

pointer

pointer是一种特殊的数据类型,用于存储变量的内存地址。指针允许你直接访问和修改变量的值,而不需要复制整个变量。

指针的定义: 指针的类型由*符号表示,*表示指向某种类型的指针。

var p *int; //定义一个指向整型的指针

获取变量的地址: 使用&符号获取一个变量的地址,并将其赋值给指针。

var a int = 10
p = &a

通过指针访问值: 通过*符号可以解引用指针,访问指针指向的值

fmt.Println(*p)	//输出10.解引用指针p获取a的值
*p = 20			//通过指针修改a的值
fmt.Println(a)	//输出20,a的值被修改

指针与函数: 指针常用于函数参数中,以避免传值带来的性能损失,尤其是当要传递大型结构体或数组时。

func increment(n *int)
{
    *n++;
}
var x int = 5
increment(&x)
fmt.Println(x)

指针与切片: 切片和映射都是引用类型,它们本身并不需要使用指针,但在某些情况下,可能会使用指针来指向他们。

func modifyslice(s *[]int){
    (*s)[0] = 100 //解引用并修改切片的第一个元素
}
slice := []int(1,2,3)
modifyslice(&slice)
fmt.Println(slice)

指针的零值:指针的零值nil,表示指针不指向任何有效的地址。

struct

定义**Struct** 使用type关键字可以定义一个新的struct类型,字段有名称和类型组成。

type Person struct{
    Name string
    Age int
}

创建Struct实例: 可以使用字面量或new关键字创建struct实例

// 使用字面量
p1 := Person{Name:"Alice",Age:10}	
// 使用new
p2 := new(Person)	
P2.Name = "Bob"
p2.Age = 25

p1不是指针,而不是一个Person类型的值,它直接存储了NameAge字段的值。

p2是一个指向Person的指针。

访问**Struct**字段: 可以使用点操作符.来访问struct的字段。

fmt.Println(p1.Name)

嵌套Struct:struct中嵌套其他struct,这可以用于构建更复杂的数据结构。

type Address struct {
    City    string
    ZipCode string
}

type Employee struct {
    Name    string
    Age     int
    Address Address
}

func main() {
    emp := Employee{
        Name: "Emma",
        Age:  28,
        Address: Address{
            City:    "New York",
            ZipCode: "10001",
        },
    }
    fmt.Println(emp.Name)          // 输出:Emma
    fmt.Println(emp.Address.City)  // 输出:New York
}

Struct的方法: 可以为struct定义方法,以便对结构体实力进行操作。方法接收者可以是结构体的值或者指针。

func (p Person) Greet() string{
    return "Hello, my name is " + p.Name
}
func (p *Person) HaveBirthday(){
    p.Age++
}
p1.HaveBirthday()	
fmt.Println(p1.Greet())

空值Struct 结构体的零值是每个字段类型的零值

var p3 Person

匿名字段和嵌套结构体: Go还支持匿名字段,通常用于实现类似继承的功能。

package main

import "fmt"

type ContactInfo struct {
	Email string
	Phone string
}
type Manager struct {
	Name string
	Age  int
	ContactInfo
}

func main() {
	m := Manager{
		Name: "Lihao",
		Age:  18,
		ContactInfo: ContactInfo{
			Email: "abc@124.com",
			Phone: "13800138000",
		},
	}
	fmt.Println(m)
}

在这个例子中,ContactInfo作为Manager的匿名字段,可以直接通过m.Emailm.Phone访问,不需要显示地写成m.ContactInfo.Email

结构体方法: 在Go中,结构体可以定义方法,这些方法可以理解为是和某个类型绑定的函数。定义方法时,需要指定接受者(receiver),这使得方法可以作用于特定的结构体实例。

type Rectangle struct {
	width, height float64
}

func (r Rectangle) Area() float64 {
	return r.width * r.height
}

func (r *Rectangle) SetWidth(newWidth float64) {
	r.width = newWidth
}

func main() {
	rect := Rectangle{width: 20, height: 10}
	fmt.Println(rect.width, rect.height)
	rect.SetWidth(10)
	fmt.Println(rect.width, rect.height)
}

error

error是一种内置的接口类型,通常用于表示错误信息。Go语言显示地处理错误,而不是使用异常机制。

错误处理是显示,设计优点:

  • 错误不会被隐式地吞掉,程序的每个部分都清楚地知道哪里出了问题。
  • 可以根据不同的错误类型采取不同的处理策略,保持程序的健壮性。

error类型:error类型是一个接口类型

type error interface{
    Error() string
}

error接口只有一个方法:Error(),它返回一个string类型的错误描述信息。

任何实现了Error()方法的类型都可以作为error类型使用。

创建和返回错误: 在Go中,通过实现error接口的方式,返回一个错误。

import "errors"
func doSomething() error{
    return errors.New("something went wrong")
}
func main(){
    err := doSomething()
    if err != nil{
        fmt.Println("Error:",err)
    }
}

自定义错误类型: Go允许定义一个结构体类型,并实现Error()方法来创建更复杂的错误类型。

type MyError struct {
	Code    int
	Message string
}

func (e *MyError) Error() string {
	return fmt.Sprintf("code:%d message:%s", e.Code, e.Message)
}
func doSomething() error {
	return &MyError{Code: 404, Message: "Not Found"}
}
func main() {
	err := doSomething()
	if err != nil {
		fmt.Println("Error:", err)
	}
}

检查包装的错误:error.Is来判断某个错误是否是特定类型,或是是否由某个错误引起的。

var ErrNotFound = errors.New("not found")

func doSomething() error {
    return fmt.Errorf("doSomething failed: %w", ErrNotFound)
}

func main() {
    err := doSomething()
    if errors.Is(err, ErrNotFound) {
        fmt.Println("Error: not found")
    }
}

string

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

fmt

结构体格式化(带字段名)

// 带字段名
fmt.Printf("p=%+v\n", p) // p={x:1 y:2}
// 带类型信息
fmt.Printf("p=%#v\n", p) // p=main.point{x:1, y:2}
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
}

json

Go中的JSON基本操作:

  • json.Marshal()用于将Go的数据结构编码为JSON字符串。
type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

func main() {
    p := Person{Name: "Alice", Age: 25}
    jsonData, err := json.Marshal(p)
    if err != nil {
        fmt.Println("Error encoding JSON:", err)
        return
    }
    fmt.Println(string(jsonData)) // 输出:{"name":"Alice","age":25}
}

结构体标签(tag):

  • 在结构体字段定义时,我们使用了标签(如 json:"name"),这表示将结构体字段 Name 在 JSON 中表示为键 "name"。结构体标签的作用是控制 JSON 序列化和反序列化时的字段名称。
  • **json.Unmarshal()**用于将JSON字符串解码为Go的数据结构。
type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

func main() {
    jsonString := `{"name": "Bob", "age": 30}`
    var p Person
    err := json.Unmarshal([]byte(jsonString), &p)
    if err != nil {
        fmt.Println("Error decoding JSON:", err)
        return
    }
    fmt.Println(p.Name, p.Age) // 输出:Bob 30
}
  • 使用 json.Unmarshal([]byte(jsonString), &p) 将 JSON 字符串解码为 Person 结构体。
  • 注意:json.Unmarshal 需要传入的是 []byte 类型的数据,所以需要将字符串转换为字节数组。
  • 解码时需要传递指向结构体的指针(&p),以便 json.Unmarshal 可以直接修改结构体内容。

处理动态JSON: JSON结构不固定,当需要处理更灵活的JSON数据时,可以使用 map[string]interface{}来处理。

func main() {
    jsonString := `{"name": "Charlie", "age": 32, "skills": 
    ["Go", "Python", "JavaScript"]}`
    var data map[string]interface{}
    err := json.Unmarshal([]byte(jsonString), &data)
    if err != nil {
        fmt.Println("Error decoding JSON:", err)
        return
    }
    fmt.Println(data["name"])                  // 输出:Charlie
    fmt.Println(data["age"])                   // 输出:32
    fmt.Println(data["skills"].([]interface{})) // 输出:[Go Python JavaScript]
}

在这个例子中,data是一个map[string]interface{},可以存储任意键值对。

由于 JSON 数据的结构未知,因此字段类型是 interface{},这意味着我们需要进行类型断言(如 data["skills"].([]interface{}))来访问具体类型的数据。

** 结构体字段的导出 :** json.Marshaljson.Unmarshal 只能处理结构体中 导出的字段,即首字母大写的字段。首字母小写的字段是私有的,无法被编码或解码。

type Person struct {
    name string `json:"name"` // 错误:字段不可导出
    Age  int    `json:"age"`
}

func main() {
    p := Person{name: "David", Age: 35}
    jsonData, err := json.Marshal(p)
    if err != nil {
        fmt.Println("Error encoding JSON:", err)
        return
    }
    fmt.Println(string(jsonData)) // 输出:{"age":35},字段 "name" 未被编码
}

time

time.Time类型:time.Time 是表示时间的核心类型,它表示了一个具体的时间点。可以使用 time.Now() 函数获取当前时间。

默认情况下,time.Now() 获取的是本地时间。如果你需要获取某个特定时区的时间,可以使用 time.LoadLocation 来实现。

currentTime := time.Now()
loc, err := time.LoadLocation("America/New_York")

格式化时间: time 包提供了 Format 方法来将时间格式化为特定的字符串表示形式。在 Go 中,格式化时间的方式非常特别,它使用一个参考时间:2006-01-02 15:04:05 来定义格式。这些数字是 Go 中的魔法时间,代表了 2006 年 1 月 2 日 15:04:05,开发人员通过这种方式来定义期望的格式。

func main() {
    currentTime := time.Now()

    // 常见的格式化示例
    fmt.Println(currentTime.Format("2006-01-02 15:04:05"))  // 标准格式,输出类似:2024-11-05 14:23:01
    fmt.Println(currentTime.Format("2006/01/02 03:04:05 PM")) // 带 AM/PM 的 12 小时制,输出类似:2024/11/05 02:23:01 PM
    fmt.Println(currentTime.Format("02-Jan-2006"))           // 输出:05-Nov-2024
}

** 解析时间 :** time.Parse()从字符串中解析时间。

func main() {
    timeString := "2024-11-05 14:23:01"
    parsedTime, err := time.Parse("2006-01-02 15:04:05", timeString)
    if err != nil {
        fmt.Println("解析时间出错:", err)
        return
    }
    fmt.Println("解析后的时间:", parsedTime)
}

time.Parse 根据传入的格式解析时间字符串,如果格式不匹配会返回错误。

时间的计算和操作:

time 包还提供了对时间进行算术操作的方法,比如增加、减少时间等。

  • 增加时间:使用 Add 方法。
  • 减少时间:传递负的 DurationAdd 方法
func main() {
    currentTime := time.Now()
    fmt.Println("当前时间:", currentTime)

    // 增加 2 小时
    twoHoursLater := currentTime.Add(2 * time.Hour)
    fmt.Println("两小时后的时间:", twoHoursLater)

    // 减少 30 分钟
    thirtyMinutesAgo := currentTime.Add(-30 * time.Minute)
    fmt.Println("30 分钟前的时间:", thirtyMinutesAgo)
}

在这里,time.Hourtime.Minute 等都是 time.Duration 类型,代表时间的长度。time.Duration 其实是 int64 类型,单位是纳秒(ns)。

时间差计算:

Sub 方法计算两个时间之间的差值,返回的是 time.DurationSub 方法返回两个时间点之间的差值,可以进一步转换为秒、毫秒等。

func main() {
    start := time.Now()

    // 模拟某些操作的延迟
    time.Sleep(2 * time.Second)

    end := time.Now()
    duration := end.Sub(start)
    fmt.Println("操作耗时:", duration) // 输出:操作耗时: 2s
}

Sub 方法返回两个时间点之间的差值,可以进一步转换为秒、毫秒等。

定时器与延迟:

  • 延迟:可以使用 time.Sleep 函数使程序暂停一段时间。
func main() {
    fmt.Println("等待 2 秒...")
    time.Sleep(2 * time.Second)
    fmt.Println("2 秒后程序继续执行")
}
  • 定时器:可以使用 time.NewTimer 创建定时器,定时器到期时会向通道发送时间信息。
func main() {
    timer := time.NewTimer(3 * time.Second)
    fmt.Println("计时器开始...")

    <-timer.C // 阻塞直到定时器通道发送信号
    fmt.Println("计时器到期,继续执行")
}

时间戳 : Go 中也可以获取 Unix 时间戳,这个时间戳表示从 1970 年 1 月 1 日 00:00:00 UTC 到当前时间所经过的秒数或毫秒数。

定时任务(Ticker):使用 time.Ticker 可以实现每隔一段时间执行某个任务的功能。Ticker 会周期性地向通道发送当前时间。

func main() {
    ticker := time.NewTicker(1 * time.Second) // 每秒触发一次
    defer ticker.Stop()

    for i := 0; i < 5; i++ {
        <-ticker.C
        fmt.Println("Ticker 触发,当前时间:", time.Now())
    }
}

在这个例子中,NewTicker(1 * time.Second) 每秒向通道发送一次信号,程序每次读取到信号后会打印当前时间。

strconv

  • strconv 包提供了 字符串与基本数据类型之间转换 的一组函数,是处理输入输出时非常常用的工具。
  • 常用的函数包括:
  • 整数转换Atoi(字符串到整数)、Itoa(整数到字符串)。
  • 浮点数转换ParseFloat(字符串到浮点数)、FormatFloat(浮点数到字符串)。
  • 布尔值转换ParseBool(字符串到布尔值)、FormatBool(布尔值到字符串)。
  • 进制转换ParseInt(字符串到整数,支持指定进制)、FormatInt(整数到字符串,支持指定进制)。

env

  • 读取环境变量:使用 os.Getenv() 来读取指定环境变量的值,如果环境变量不存在,返回空字符串。
  • 设置环境变量:使用 os.Setenv() 来设置环境变量。
  • 删除环境变量:使用 os.Unsetenv() 来删除某个环境变量。
  • 遍历环境变量:使用 os.Environ() 可以获取系统中所有的环境变量。
  • 提供默认值:在读取环境变量时,可以通过编写辅助函数来提供默认值,以确保在环境变量未设置的情况下程序也能正常运行。
  • .env文件管理:使用 .env 文件集中管理环境变量,并使用第三方库(如 godotenv)加载环境变量,可以方便地为开发、测试和生产环境提供同的配置。