仓库源文站点原文


title: golang基础学习 description: golang基础语法学习(VScode+ubuntu22.04.3LTS) slug: golangStudytest date: 2023-09-07 14:34:00+0800 image: 后端8小时转职Golang工程师.png categories:

- techStudy

tags:

- go    

weight: 1 # You can add weight to some posts to override the default sorting (date descending)

comments: true

license: flase

math: true

toc: true

style:

keywords:

readingTime:


Go语言学习


--


1.main

--

  package main
//定义了包名。你必须在源文件中非注释的第一行指明这个文件属于哪个包,如:package main。package main表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包
  import "fmt"
//告诉 Go 编译器这个程序需要使用 fmt 包(的函数,或其他元素),fmt 包实现了格式化 IO(输入/输出)的函数
  func main() {
          fmt.Println("Hello Go")//Print不加\n,Println自带加\n
  }
//程序开始执行的函数。main 函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数(如果有 init() 函数则会先执行该函数)
//运行
go run test1_hello.go 
Hello Go

2.变量

--

2.1 指定变量类型

import "fmt"

func main() { var a int fmt.Printf(" = %d\n", a) }


--

### 2.2 根据值自行判定变量类型
```go
var v_name = value

--

2.3 省略var,

// 例如 var a int = 10 var b = 10 c : = 10

--

### 2.4 例子

--

```go
package main


import "fmt"


func main() {
        //第一种 使用默认值
        var a int
        fmt.Printf("a = %d\n", a)


        //第二种
        var b int = 10
        fmt.Printf("b = %d\n", b)


        //第三种 省略后面的数据类型,自动匹配类型
        var c = 20
        fmt.Printf("c = %d\n", c)


        //第四种 省略var关键字
        d := 3.14
        fmt.Printf("d = %f\n", d)
}
go run var.go 
a = 0
b = 10
c = 20
d = 3.140000

--

\\多变量声明
package main

import "fmt"


var x, y int
var ( //这种分解的写法,一般用于声明全局变量
        a int
        b bool
)


var c, d int = 1, 2
var e, f = 123, "liudanbing"


//这种不带声明格式的只能在函数体内声明
//g, h := 123, "需要在func函数体内实现"


func main() {
        g, h := 123, "需要在func函数体内实现"
        fmt.Println(x, y, a, b, c, d, e, f, g, h)


        //不能对g变量再次做初始化声明
        //g := 400


        _, value := 7, 5  //实际上7的赋值被废弃,变量 _  不具备读特性
        //fmt.Println(_) //_变量的是读不出来的
        fmt.Println(value) //5
}
go run mutiVar.go 

0 0 0 false 1 2 123 liudanbing 123 需要在func函数体内实现
5

3.常量

--

--

3.1 常量定义格式

const identifier [type] = value
//显示类型定义
const b string = "abc"
//隐式类型定义
const b = "abc"

--

3.2 常量枚举

const (
    Unknown = 0
    Female = 1
    Male = 2
)//数字 0、1 和 2 分别代表未知性别、女性和男性

--

3.3 常量表达式

//常量可以用len(), cap(), unsafe.Sizeof()常量计算表达式的值。常量表达式中,函数必须是内置函数,否则编译不过:
package main


import "unsafe"
const (
    a = "abc"
    b = len(a)
    c = unsafe.Sizeof(a)
)


func main(){
    println(a, b, c)
}
abc, 3, 16//字符串类型在 go 里是个结构, 包含指向底层数组的指针和长度,这两部分每部分都是 8 个字节,所以字符串类型大小为 16 个字节。

--

3.4 常量iota

type Allergen int

const (
    _           = iota                   // ignore first value by assigning to blank identifier
    IgEggs Allergen = 1          // 1 << 0 which is 00000001
    IgChocolate                         // 1 << 1 which is 00000010
    IgNuts = 1 << iota                  // 1 << 2 which is 00000100
    IgStrawberries                      // 1 << 3 which is 00001000
    IgShellfish                         // 1 << 4 which is 00010000
)
1 2 4 8 16

--

const (
    Apple, Banana = iota + 1, iota + 2
    Cherimoya, Durian
    Elderberry, Fig
)
// Apple: 1
// Banana: 2
// Cherimoya: 2
// Durian: 3
// Elderberry: 3
// Fig: 4

4.函数

func function_name( [parameter list] ) [return_types] {
   函数体
}

--

4.1 函数多返回值

package main


import "fmt"


func swap(x, y string) (string, string) {
   return y, x
}


func main() {
   a, b := swap("Mahesh", "Kumar")
   fmt.Println(a, b)
}
//Kumar Mahesh

--

4.2 ini/main和import

--

--

4.3 go指针

package main


import "fmt"


func main() {
   /* 定义局部变量 */
   var a int = 100
   var b int= 200


   fmt.Printf("交换前,a 的值 : %d\n", a )
   fmt.Printf("交换前,b 的值 : %d\n", b )


   /* 调用 swap() 函数
   * &a 指向 a 指针,a 变量的地址
   * &b 指向 b 指针,b 变量的地址
   */
   swap(&a, &b)


   fmt.Printf("交换后,a 的值 : %d\n", a )
   fmt.Printf("交换后,b 的值 : %d\n", b )
}


func swap(x *int, y *int) {
   var temp int
   temp = *x    /* 保存 x 地址上的值 */
   *x = *y      /* 将 y 值赋给 x */
   *y = temp    /* 将 temp 值赋给 y */
}

--

4.4 defer函数

defer在Go里可以放在某个函数或者方法调用的前面,让该函数或方法延迟执行

//语法
defer function([parameter_list]) // 延迟执行函数
defer method([parameter_list]) // 延迟执行方法

defer在函数体内执行,在函数A内调用了defer func(),只要defer func()这行代码被执行到,func这个函数就会被延迟到函数A return或panic之前执行。

--

func Demo(){
    defer fmt.Println("1")
    defer fmt.Println("2")
    defer fmt.Println("3")
    defer fmt.Println("4")
}
func main() {
    Demo()
}
/*
4
3
2
1
*/

--

4.5 recover错误拦截

运行时panic异常一旦被引发就会导致程序崩溃。

Go语言提供了专用于“拦截”运行时panic的内建函数“recover”。它可以是当前的程序从运行时panic的状态中恢复并重新获得流程控制权。

--

package main

import "fmt"

func Demo(i int) {
    //定义10个元素的数组
    var arr [10]int
    //错误拦截要在产生错误前设置
    defer func() {
        //设置recover拦截错误信息
        err := recover()
        //产生panic异常  打印错误信息
        if err != nil {
            fmt.Println(err)
        }
    }()
    //根据函数参数为数组元素赋值
    //如果i的值超过数组下标 会报错误:数组下标越界
    arr[i] = 10

}

func main() {
    Demo(10)
    //产生错误后 程序继续
    fmt.Println("程序继续执行...")
}

//输出:
//runtime error: index out of range
//程序继续执行...

5.输入输出

--

--

5.1 输出

//Print系列函数会将内容输出到系统的标准输出,区别在于Print函数直接输出内容,Printf函数支持格式化输出字符串,Println函数会在输出内容的结尾添加一个换行符。
func Print(a ...interface{}) (n int, err error)

func Printf(format string, a ...interface{}) (n int, err error)

func Println(a ...interface{}) (n int, err error)


func main() {
  name := "沙河小王子"

    fmt.Print("在终端打印该信息。")
    fmt.Printf("我是:%s\n", name)
    fmt.Println("在终端打印单独一行显示")
}

--

5.2 输入

func Scan(a ...interface{}) (n int, err error)

func Scanf(format string, a ...interface{}) (n int, err error)

func Scanln(a ...interface{}) (n int, err error)


fmt.Scan(&name, &age, &isMale)//fmt.Scan在没有扫描完所有变量之前是不会结束扫描的
fmt.Scanf("name:%s age:%d isMale:%v", &name, &age, &isMale)//遇到换行即结束扫描,如果还有没输入的变量值,该变量会按默认值处理
fmt.Scanln(&name, &age, &married)//遇到换行即结束扫描,如果还有没输入的变量值,该变量会按默认值处理

6.slice切片、map

--

6.1 数组

6.2 slice

//make()创建切片 var slice1 []type = make([]type, len) //简写为 slice1 := make([]type, len, cap) //len有值的元素数量 切片长度 //cap可省略,切片容量


--

应用举例
```go
//切片在未初始化之前默认为 nil,长度为 0

s :=[] int {1,2,3 }
//直接初始化切片,[]表示是切片类型,{1,2,3}初始化值依次是1,2,3.其cap=len=3

s := arr[:]
//初始化切片s,是数组arr的引用

s := arr[startIndex:endIndex]
//将arr中从下标startIndex到endIndex-1 下的元素创建为一个新的切片,是数组arr的部分引用

s1 := s[startIndex:endIndex]
//通过切片s初始化切片s1,仍然是引用

//len() 获取长度 cap() 获取容量
len(x)
cap(x)

--

增加切片的容量 创建新的大切片,拷贝原分片的内容

var numbers []int
/* 同时添加多个元素 */
numbers = append(numbers, 2,3,4)
printSlice(numbers)

/* 拷贝 numbers 的内容到 numbers1 */
copy(numbers1,numbers)
printSlice(numbers1)

--

6.3 map

一种无序的键值对集合

//第一种声明
var test1 map[string]string
//在使用map前,需要先make,make的作用就是给ma配数据空间
test1 = make(map[string]string, 10) 

//第二种声明
test2 := make(map[string]string)

//第三种声明
test3 := map[string]string{
    "one" : "php",
    "two" : "golang",
    "three" : "java",
}

// 增改键值对
myMap["apple"] = 5

// 删除键值对
delete(myMap, "apple")

// 检索值
value = myMap["banana"]

--

//嵌套map
language := make(map[string]map[string]string)
language["php"] = make(map[string]string, 2)
language["php"]["id"] = "1"
language["php"]["desc"] = "php是世界上最美言"
language["golang"] = make(map[string]string2)
language["golang"]["id"] = "2"
language["golang"]["desc"] = "golang抗并发good"

7.结构体

--

7.1 结构体定义

type MyStruct struct {
    Field1 Type1
    Field2 Type2
    // 可以有更多字段
}
//例如
type Person struct {
    FirstName string
    LastName  string
    Age       int
}
//创建实例
p1 := Person{
    FirstName: "John",
    LastName:  "Doe",
    Age:       30,
}
//访问结构体字段
fmt.Println(p1.FirstName) // 输出 "John"
fmt.Println(p2.LastName)  // 输出 "Smith"

--

7.2 结构体方法

func (receiver ReceiverType) MethodName() ReturnType {
    // 方法的实现
}
//例如
func (p Person) FullName() string {
    return p.FirstName + " " + p.LastName
}
//调用
fmt.Println(p1.FullName()) // 输出 "John Doe"
fmt.Println(p2.FullName()) // 输出 "Alice Smith"

//使用指针接收器,方法接收的是结构体的指针而不是副本,使得修改能够影响原始结构体
func (receiver *ReceiverType) MethodName() {
    // 方法的实现
}

func (p *Person) IncrementAge() {
    p.Age++
}

p1.IncrementAge()
fmt.Println(p1.Age) // 输出 31

8.方法值和方法表达式

--

8.1 方法值

方法值是一种将方法绑定到特定实例的函数。它允许您将一个方法作为普通的函数值进行传递、存储和调用。

package main

import "fmt"

type Circle struct {
    Radius float64
}

func (c Circle) Area() float64 {
    return 3.14 * c.Radius * c.Radius
}

func main() {
    c := Circle{Radius: 5}

    // 方法值的用法
    areaFunc := c.Area
    area := areaFunc()

    fmt.Printf("圆的面积:%f\n", area)
}

--

8.2 方法表达式

方法表达式允许您将方法绑定到类型而不是实例,并将其作为函数值进行传递、存储和调用 接口类型可以由多种不同的类型实现。

package main
import "fmt"
type Geometry interface {
    Area() float64
}

type Circle struct {
    Radius float64
}

func (c Circle) Area() float64 {
    return 3.14 * c.Radius * c.Radius
}

func CalculateArea(g Geometry) float64 {
    return g.Area()
}

func main() {
    c := Circle{Radius: 5}

    // 方法表达式的用法
    areaFunc := (*Circle).Area
    area := areaFunc(&c)

    fmt.Printf("圆的面积:%f\n", area)

    // 使用接口调用方法表达式
    area2 := CalculateArea(&c)
    fmt.Printf("通过接口计算的圆的面积:%f\n", area2)
}

--

8.3

方法值允许将方法绑定到特定实例并将其用作函数值 方法表达式允许将方法绑定到类型并将其用作函数值。

这两个概念对于 Go 中的面向对象编程和接口实现非常有用。


9.interface与类型断言

--

9.1 interface

//定义接口
type Person interface {
    // 声明方法
    method1(参数列表)返回值列表
    method2(参数列表)返回值列表
}

//实现接口
func (t 自定义类型)method1(参数列表)返回值列表 {
    //方法实现
}
func (t 自定义类型)method2(参数列表)返回值列表 {
    //方法实现
}

--

//在Go中,接口的实现是隐式的。如果一个类型包含了接口中定义的所有方法,那么它被视为实现了该接口。
type Shape interface {
    Area() float64
    Perimeter() float64
}

type Circle struct {
    Radius float64
}

func (c Circle) Area() float64 {
    return math.Pi * c.Radius * c.Radius
}

func (c Circle) Perimeter() float64 {
    return 2 * math.Pi * c.Radius
}

//使用接口可以编写通用的代码,不依赖于具体的类型。例如,可以编写一个函数来计算任何形状的面积和周长:
func PrintShapeDetails(s Shape) {
    fmt.Printf("Area: %f\n", s.Area())
    fmt.Printf("Perimeter: %f\n", s.Perimeter())
}

func main() {
    c := Circle{Radius: 5}
    PrintShapeDetails(c)
}

9.2 类型断言

value, ok := i.(int) if ok { fmt.Printf("i 是一个整数: %d\n", value) } else { fmt.Println("i 不是一个整数") }

//直接断言使用 var a interface{} fmt.Println("Where are you,Jonny?", a.(string))

### 9.3 工厂函数
```go
//创建并返回一个新的类型实例
package main

import "fmt"

// 定义一个接口
type Shape interface {
    Area() float64
}

// 定义一个类型,实现了 Shape 接口
type Circle struct {
    Radius float64
}

// Circle 类型的方法,用于计算面积
func (c Circle) Area() float64 {
    return 3.14159265359 * c.Radius * c.Radius
}

// 工厂函数,用于创建 Circle 类型的实例
func NewCircle(radius float64) *Circle {
    return &Circle{Radius: radius}
}

func main() {
    // 使用工厂函数创建 Circle 对象
    c1 := NewCircle(5.0)
    c2 := NewCircle(2.0)

    // 调用 Circle 对象的方法
    area1 := c1.Area()
    area2 := c2.Area()

    fmt.Printf("Circle 1 Area: %f\n", area1)
    fmt.Printf("Circle 2 Area: %f\n", area2)
}

10.反射reflect

--

--

比如:

package main

import "fmt"

type Reader interface {
    ReadBook()
}

type Writer interface {
    WriteBook()
}

//具体类型
type Book struct {
}

func (this *Book) ReadBook() {
    fmt.Println("Read a book.")
}

func (this *Book) WriteBook() {
    fmt.Println("Write a book.")
}

func main() {
    b := &Book{}

    var r Reader
    r = b

    r.ReadBook()

    var w Writer
    w = r.(Writer)
    w.WriteBook()
}

--

reflect.TypeOf()是获取pair中的type reflect.ValueOf()获取pair中的value 示例如下:

package main

import (
    "fmt"
    "reflect"
)

func main() {
    var num float64 = 1.2345

    fmt.Println("type: ", reflect.TypeOf(num))
    fmt.Println("value: ", reflect.ValueOf(num))
}

运行结果:
type:  float64
value:  1.2345

--

从已知原有类型创建新的变量

realValue := value.Interface().(已知的类型)

package main

import (
    "fmt"
    "reflect"
)

func main() {
    var num float64 = 1.2345

    pointer := reflect.ValueOf(&num)
    value := reflect.ValueOf(num)

    // 可以理解为“强制转换”,但是需要注意的时候,转换的时候,如果转换的类型不完全符合,则直接panic
    // Golang 对类型要求非常严格,类型一定要完全符合
    // 如下两个,一个是*float64,一个是float64,如果弄混,则会panic
    convertPointer := pointer.Interface().(*float64)
    convertValue := value.Interface().(float64)

    fmt.Println(convertPointer)
    fmt.Println(convertValue)
}

运行结果:
0xc42000e238
1.2345

--

未知原有类型

package main

import (
    "fmt"
    "reflect"
)

type User struct {
    Id   int
    Name string
    Age  int
}

func (u User) ReflectCallFunc() {
    fmt.Println("Allen.Wu ReflectCallFunc")
}

func main() {

    user := User{1, "Allen.Wu", 25}

    DoFiledAndMethod(user)

}

// 通过接口来获取任意参数,然后一一揭晓
func DoFiledAndMethod(input interface{}) {

    getType := reflect.TypeOf(input)
    fmt.Println("get Type is :", getType.Name())

    getValue := reflect.ValueOf(input)
    fmt.Println("get all Fields is:", getValue)

    // 获取方法字段
    // 1. 先获取interface的reflect.Type,然后通过NumField进行遍历
    // 2. 再通过reflect.Type的Field获取其Field
    // 3. 最后通过Field的Interface()得到对应的value
    for i := 0; i < getType.NumField(); i++ {
        field := getType.Field(i)
        value := getValue.Field(i).Interface()
        fmt.Printf("%s: %v = %v\n", field.Name, field.Type, value)
    }

    // 获取方法
    // 1. 先获取interface的reflect.Type,然后通过.NumMethod进行遍历
    for i := 0; i < getType.NumMethod(); i++ {
        m := getType.Method(i)
        fmt.Printf("%s: %v\n", m.Name, m.Type)
    }
}

运行结果:
get Type is : User
get all Fields is: {1 Allen.Wu 25}
Id: int = 1
Name: string = Allen.Wu
Age: int = 25
ReflectCallFunc: func(main.User)

11.结构体标签


12.goroutine

--

--



package main

import (
"fmt"
"runtime"
)

func main() {
    go func() {
        defer fmt.Println("A.defer")

        func() {
            defer fmt.Println("B.defer")
            runtime.Goexit() // 终止当前 goroutine, import "runtime"
            fmt.Println("B") // 不会执行
        }()

        fmt.Println("A") // 不会执行
    }()       //不要忘记()

    //死循环,目的不让主goroutine结束
    for {
    }
}

13.channel

--

--

13.1定义channel变量

//chan是创建channel所需使用的关键字。Type 代表指定channel收发数据的类型。
    make(chan Type)  //等价于make(chan Type, 0)
    make(chan Type, capacity)
//当 参数capacity= 0 时,channel 是无缓冲阻塞读写的;当capacity > 0 时,channel 有缓冲、是非阻塞的,直到写满 capacity个元素才阻塞写入。

--

13.2接收发送数据

--

默认情况下,channel接收和发送数据都是阻塞的,除非另一端已经准备好,这样就使得goroutine同步变的更加的简单,而不需要显式的lock。

package main

import (
    "fmt"
)

func main() {
    c := make(chan int)

    go func() {
        defer fmt.Println("子go程结束")

        fmt.Println("子go程正在运行……")

        c <- 666 //666发送到c
    }()

    num := <-c //从c中接收数据,并赋值给num

    fmt.Println("num = ", num)
    fmt.Println("main go程结束")
}

--

无缓冲的channel

import ( "fmt" "time" )

func main() { c := make(chan int, 0) //创建无缓冲的通道 c

//内置函数 len 返回未被读取的缓冲元素数量,cap 返回缓冲区大小
fmt.Printf("len(c)=%d, cap(c)=%d\n", len(c), cap(c))

go func() {
    defer fmt.Println("子go程结束")

    for i := 0; i < 3; i++ {
        c <- i
        fmt.Printf("子go程正在运行[%d]: len(c)=%d, cap(c)=%d\n", i, len(c), cap(c))
    }
}()

time.Sleep(2 * time.Second) //延时2s

for i := 0; i < 3; i++ {
    num := <-c //从c中接收数据,并赋值给num
    fmt.Println("num = ", num)
}

fmt.Println("main进程结束")

}


--

有缓冲的channel
有缓冲的通道(buffered channel)是一种在被接收前能存储一个或者多个数据值的通道。

这种类型的通道并不强制要求 goroutine 之间必须同时完成发送和接收。通道会阻塞发送和接收动作的条件也不同。

只有通道中没有要接收的值时,接收动作才会阻塞。

只有通道没有可用缓冲区容纳被发送的值时,发送动作才会阻塞。

这导致有缓冲的通道和无缓冲的通道之间的一个很大的不同:无缓冲的通道保证进行发送和接收的 goroutine 会在同一时间进行数据交换;有缓冲的通道没有这种保证。

```go
func main() {
    c := make(chan int, 3) //带缓冲的通道

    //内置函数 len 返回未被读取的缓冲元素数量, cap 返回缓冲区大小
    fmt.Printf("len(c)=%d, cap(c)=%d\n", len(c), cap(c))

    go func() {
        defer fmt.Println("子go程结束")

        for i := 0; i < 3; i++ {
            c <- i
            fmt.Printf("子go程正在运行[%d]: len(c)=%d, cap(c)=%d\n", i, len(c), cap(c))
        }
    }()

    time.Sleep(2 * time.Second) //延时2s
    for i := 0; i < 3; i++ {
        num := <-c //从c中接收数据,并赋值给num
        fmt.Println("num = ", num)
    }
    fmt.Println("main进程结束")
}

--

13.3 关闭channel

import ( "fmt" )

func main() { c := make(chan int)

go func() {
    for i := 0; i < 5; i++ {
        c <- i
    }
    close(c)
}()

for {
    //ok为true说明channel没有关闭,为false说明管道已经关闭
    if data, ok := <-c; ok {
        fmt.Println(data)
    } else {
        break
    }
}

fmt.Println("Finished")

}


--

### 13.4单向channel及应用
声明
```go
var ch1 chan int       // ch1是一个正常的channel,是双向的
var ch2 chan<- float64 // ch2是单向channel,只用于写float64数据
var ch3 <-chan int     // ch3是单向channel,只用于读int数据

//可以将 channel 隐式转换为单向队列,只收或只发,不能将单向 channel 转换为普通 channel:

    c := make(chan int, 3)
    var send chan<- int = c // send-only
    var recv <-chan int = c // receive-only
    send <- 1
    //<-send //invalid operation: <-send (receive from send-only type chan<- int)
    <-recv
    //recv <- 2 //invalid operation: recv <- 2 (send to receive-only type <-chan int)

    //不能将单向 channel 转换为普通 channel
    d1 := (chan int)(send) //cannot convert send (type chan<- int) to type chan int
    d2 := (chan int)(recv) //cannot convert recv (type <-chan int) to type chan int

14.封装

--

package model

import(
    "fmt"
)

type person struct{
    Name string
    age int //其他包不能访问
    sal float64 //其他包不能访问
}

//写一个工程模式的函数,相对于一个构造函数
func NewPerson(name string) *person{
    return &person{
        Name : name
    }
}

//为了访问age和sal我们编写一对SetXxx的方法和GetXxx的方法
func (p *person) SetAge(age int) {
    if age > 0 && age < 150 {
        p.age = age
    }else{
        fmt.Println("年龄范围不正确")
    }
}

func (p *person) GetAge() int {
    return p.age
}

func (p *person) SetSal(sal float64) {
    if sal >= 3000 && sal <= 30000 {
        p.sal = sal
    }else{
        fmt.Println("薪水范围不正确")
    }
}

func (p *person) GetSal() float64 {
    return p.sal
}

15.Select

--


16. GO Modules

--

GO111MODULE

Go语言提供了 GO111MODULE这个环境变量来作为 Go modules 的开关,其允许设置以下参数:

● auto:只要项目包含了 go.mod 文件的话启用 Go modules,目前在 Go1.11 至 Go1.14 中仍然是默认值。 ● on:启用 Go modules,推荐设置,将会是未来版本中的默认值。 ● off:禁用 Go modules,不推荐设置。

$ go env -w GO111MODULE=on

--

GOPROXY

这个环境变量主要是用于设置 Go 模块代理(Go module proxy),其作用是用于使 Go 在后续拉取模块版本时直接通过镜像站点来快速拉取。

GOPROXY 的默认值是:https://proxy.golang.org,direct

proxy.golang.org国内访问不了,需要设置国内的代理.

● 阿里云 https://mirrors.aliyun.com/goproxy/ ● 七牛云 https://goproxy.cn,direct

$ go env -w GOPROXY=https://goproxy.cn,direct
//假设你不想使用,也可以将其设置为 “off” ,这将会禁止 Go 在后续操作中使用任何 Go 模块代理。

--

GONOPROXY/GONOSUMDB/GOPRIVATE

这三个环境变量都是用在当前项目依赖了私有模块,例如像是你公司的私有 git 仓库,又或是 github 中的私有库,都是属于私有模块,都是要进行设置的,否则会拉取失败。

更细致来讲,就是依赖了由 GOPROXY 指定的 Go 模块代理或由 GOSUMDB 指定 Go checksum database 都无法访问到的模块时的场景。

而一般建议直接设置 GOPRIVATE,它的值将作为 GONOPROXY 和 GONOSUMDB 的默认值,所以建议的最佳姿势是直接使用 GOPRIVATE。

并且它们的值都是一个以英文逗号 “,” 分割的模块路径前缀,也就是可以设置多个,例如:

$ go env -w GOPRIVATE="git.example.com,github.com/eddycjy/mquote"

设置后,前缀为 git.xxx.com 和 github.com/eddycjy/mquote 的模块都会被认为是私有模块。

如果不想每次都重新设置,我们也可以利用通配符,例如:

$ go env -w GOPRIVATE="*.example.com"

这样子设置的话,所有模块路径为 example.com 的子域名(例如:git.example.com)都将不经过 Go module proxy 和 Go checksum database,需要注意的是不包括 example.com 本身。

--

使用Go Modules初始化项目

  1. 开启 go modules

    $ go env -w GO111MODULE=on
    
  2. 初始化项目

//创建项目目录
$ mkdir -p $HOME/aceld/modules_test
$ cd $HOME/aceld/modules_test
//执行Go modules 初始化
//go mod init [myproject]
$ go mod init github.com/aceld/modules_test
go: creating new go.mod: module github.com/aceld/modules_test
//如果你的项目位于 GitHub 上的 github.com/yourusername/myproject 代码库中,你可以将模块名称设置为 github.com/yourusername/myproject,以确保模块名称在全局范围内都是唯一的。这样,其他人在使用你的项目作为依赖项时,就可以通过该模块名称来引用它。

初始化后,会:

  1. 依赖管理 ```go //添加一个依赖 go get github.com/gin-gonic/gin //这将下载依赖项并将其添加到你的go.mod文件中

//更新依赖 go get -u github.com/gin-gonic/gin

//移除依赖 go get -u -d github.com/gin-gonic/gin@none

//清理未使用的依赖 go mod tidy


在 Go Modules 模式下,项目的依赖项通常是保存在项目文件夹外部的。依赖项会被下载并保存在模块缓存目录中,而不是直接存储在项目文件夹内。

---

---

## tips
### 1.用切片操作string性能最佳
### 2.正则表达式
```go

//正则表达式包
import (
    "regexp"
)

//编译正则表达式
pattern := "ab+c"
regex, err := regexp.Compile(pattern)
if err != nil {
    // 处理编译错误
}

//匹配文本
text := "abbc"
if regex.MatchString(text) {
    fmt.Println("文本与模式匹配")
} else {
    fmt.Println("文本与模式不匹配")
}

//提取匹配内容
text := "abbc"
matched := regex.FindString(text)
fmt.Println("匹配的文本:", matched)
//匹配多次
text := "abbcabccab"
matches := regex.FindAllString(text, -1)
fmt.Println("所有匹配的文本:", matches)
//替换匹配文本
text := "abbcabccab"
replacement := "X"
newText := regex.ReplaceAllString(text, replacement)
fmt.Println("替换后的文本:", newText)