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
comments: true
--
--
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
--
import "fmt"
func main() { var a int fmt.Printf(" = %d\n", a) }
--
### 2.2 根据值自行判定变量类型
```go
var v_name = value
--
// 例如 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
--
--
const identifier [type] = value
//显示类型定义
const b string = "abc"
//隐式类型定义
const b = "abc"
--
const (
Unknown = 0
Female = 1
Male = 2
)//数字 0、1 和 2 分别代表未知性别、女性和男性
--
//常量可以用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 个字节。
--
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
func function_name( [parameter list] ) [return_types] {
函数体
}
--
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
--
--
--
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 */
}
--
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
*/
--
运行时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
//程序继续执行...
--
--
//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("在终端打印单独一行显示")
}
--
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)//遇到换行即结束扫描,如果还有没输入的变量值,该变量会按默认值处理
--
//声明数组
var arrayName [size]dataType
var balance [10]float32
//初始化数组
var numbers = [5]int{1, 2, 3, 4, 5}
numbers := [5]int{1, 2, 3, 4, 5}
// 将索引为 1 和 3 的元素初始化
balance := [5]float32{1:2.0,3:7.0}
//二维数组初始化
a := [3][4]int{
{0, 1, 2, 3} , /* 第一行索引为 0 */
{4, 5, 6, 7} , /* 第二行索引为 1 */
{8, 9, 10, 11}} /* 第三行索引为 2 */
//数组传参
void myFunction(param [10]int)//param []int
{
}
--//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)
--
一种无序的键值对集合
//第一种声明
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"
--
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"
--
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
--
方法值是一种将方法绑定到特定实例的函数。它允许您将一个方法作为普通的函数值进行传递、存储和调用。
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)
}
--
方法表达式允许您将方法绑定到类型而不是实例,并将其作为函数值进行传递、存储和调用 接口类型可以由多种不同的类型实现。
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)
}
--
方法值允许将方法绑定到特定实例并将其用作函数值 方法表达式允许将方法绑定到类型并将其用作函数值。
这两个概念对于 Go 中的面向对象编程和接口实现非常有用。
--
//定义接口
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)
}
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)
}
--
--
比如:
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)
--
--
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 {
}
}
--
--
//chan是创建channel所需使用的关键字。Type 代表指定channel收发数据的类型。
make(chan Type) //等价于make(chan Type, 0)
make(chan Type, capacity)
//当 参数capacity= 0 时,channel 是无缓冲阻塞读写的;当capacity > 0 时,channel 有缓冲、是非阻塞的,直到写满 capacity个元素才阻塞写入。
--
阻塞:由于某种原因数据没有到达,当前go程(线程)持续处于等待状态,直到条件满足,才解除阻塞。
同步:在两个或多个go程(线程)间,保持数据内容一致性的机制。
channel <- value //发送value到channel
<-channel //接收并将其丢弃
x := <-channel //从channel中接收数据,并赋值给x
x, ok := <-channel //功能同上,同时检查通道是否已关闭或者是否为空
--
默认情况下,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
无缓冲的通道(unbuffered channel)是指在接收前没有能力保存任何数据值的通道。
这种类型的通道要求发送goroutine和接收goroutine同时准备好,才能完成发送和接收操作。否则,通道会导致先执行发送或接收操作的 goroutine 阻塞等待。
这种对通道进行发送和接收的交互行为本身就是同步的。其中任意一个操作都无法离开另一个操作单独存在。 ```go package main
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进程结束")
}
--
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
--
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
}
--
select的用法与switch语言非常类似,由select开始一个新的选择块,每个选择条件由case语句来描述。
与switch语句相比,select有比较多的限制,其中最大的一条限制就是每个case语句里必须是一个IO操作,大致的结构如下:
select {
case <- chan1:
// 如果chan1成功读到数据,则进行该case处理语句
case chan2 <- 1:
// 如果成功向chan2写入数据,则进行该case处理语句
default:
// 如果上面都没有成功,则进入default处理流程
}
--
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
$ go env -w GO111MODULE=on
初始化项目
//创建项目目录
$ 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,以确保模块名称在全局范围内都是唯一的。这样,其他人在使用你的项目作为依赖项时,就可以通过该模块名称来引用它。
初始化后,会:
创建一个新的 go.mod 文件,该文件位于你的项目根目录中。这个文件将用于管理项目的依赖关系。
在 go.mod 文件中指定项目的模块名称为 myproject。
设置 Go 版本,它是当前项目所使用的 Go 语言版本。
//更新依赖 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)