go语言环境及基本常识3
Easul Lv6

特殊语法

三个点…

GOLANG
1
2
3
4
5
6
7
8
9
10
// 1. 做为形参的参数前的三个点意思是可以传0到多个参数
// 有多个同类型参数时省略使用
func DefaultBot(prepares ...BotPreparer) *Bot {}
// 2. 变量后三个点意思是将一个切片或数组变成一个一个的元素,俗称将数组打散.
// 这里是在 形参 中将参数打散
testChatRoom(prepares...)
// 这里是在 切片 中将参数打散
test := []string{"1", "2", "3", "4"}
test1 := test[0:1]
test2 := append(test1, test[3:]...)

大括号{}

GOLANG
1
2
// 大括号用于 数组 中放值
url.Values{"mod": {"desktop"}}

指针

以下为 简单理解

GOLANG
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 有了 `指针` 就可以调用 `指针对应地址` 里边的东西
// 这个意思就是将 指针b 里边的内容赋值给 指针a 的内容
*a=*b

// 指针的使用
number := 1
numberP := &number
fmt.Printf("number地址是%v,值是%v", numberP, *numberP) // 加星号后,*numberP用于读取指针的值
*numberP = 2 // 修改指针所指内容

// 给声明的指针创建一个地址
var number2P *int
number2P = new(int)
*number2P = 2
fmt.Printf("number2P地址是%v,值是%v", number2P, *number2P) // 加星号后,*numberP用于读取指针的值

关键字

type

GOLANG
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 1. 相当于类型重命名
// 这里将 string 重命名为 HandlerType
type HandlerType string

// 2. 重命名的类型可以挂载新方法
func (w HandlerType) BaseHost() string {
return "https://" + string(w)
}

// 3. 可以用于定义枚举
const (
SUCCESS HandlerType = "success"
FAIL HandlerType = "fail"
)

// 4. 也可以用于进行类型转换
fmt.Println(HandlerType("fail") == FAIL)

defer

关闭资源的时候可以用该关键字

GOLANG
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
f, err := os.Open("config.json")
if err != nil {
log.Fatalf("open config err: %v", err)
return
}
defer f.Close()

// 打开资源和关闭资源语句可以放到一起
// 因为 defer 的语句是在函数返回时被调用,即使 panic 也会被调用,类似于 finally
j.lock.Lock()
defer j.lock.Unlock()

// 对于下边的函数,先调用s.lockUnlock(),然后延迟调用s.lockUnlock()()
// 因为lockUnlock返回的是一个函数,所以也可以调用,这样就可以把打开和关闭资源放到一行
// 参考:https://blog.csdn.net/Special23/article/details/123322723
func (s *Store) Set(key string, value int) {
defer s.lockUnlock()() //①
s.data[key] = value
}

func (s *Store) lockUnlock() func() {
s.mutex.Lock()
return func() {
s.mutex.Unlock()
}

nil

没有要传的值,可以传 nil

GOLANG
1
2
3
// 将 _ 定义为 UserMessageHandler 指针类型的 nil
// https://blog.csdn.net/qq_20779397/article/details/122013951
var _ MessageHandlerInterface = (*UserMessageHandler)(nil)

range

GOLANG
1
2
3
4
5
// 如果遍历的时候,遍历的容器是一个 nil 类型,那么会进行一个空的迭代
var myStr []string = nil
for key, value := range myStr {
fmt.Println(key, value)
}

容器

数组

GOLANG
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 声明与初始化
var arr = [2]string{} // 零值为 长度为2的字符串数组,即 [2]string{}
var arr = []string{"1", "2"}
var arr = [...]string{"1", "2"}

// 迭代数组
arr := [3]string{"11", "21"}
for key, value := range arr {
fmt.Println(key, value)
}

// 获取数组长度,即有几个元素
arrLength := len(arr)
// 获取数组容量,即最多放几个元素,如果是切片则从切片在数组开始元素到最后一个元素的个数
arrLength := cap(arr)

// 创建键类型确定,值类型不确定的map(即json)
test := make(map[string]interface{})
// 创建键类型确定,值类型不确定的map数组(即json数组)
test1 := [2]map[string]interface{}{}

切片

切片不存储元素,只是对数组的引用
打印所有切片可以用 arrarr[:]
切片可以动态扩容。
放 容量 就是 数组 ,不放 容量 就是 切片 。

GOLANG
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 声明切片,零值为 nil
var slice1 []string
// 打印切片
fmt.Println(slice1)
fmt.Println(len(slice1))
fmt.Println(cap(slice1))
// 判断切片是否为空
fmt.Println(slice1 == nil)

// 初始化切片
var slice2 = []string{"1", "2"}
// make创建切片容量应预估,否则自动扩充的时候会损耗性能
const LENGTH = 3
const CAPACITY = 5
var slice3 = make([]string, LENGTH, CAPACITY)
fmt.Println(slice2)
fmt.Println(slice3)

// 切片添加元素,如果容量不够,则下次会扩充2倍容量
slice2 = append(slice2, "asdf")
fmt.Println(slice2)
// 数组的切片添加数据后,会覆盖原来位置的数组数据
arr := []string{"1", "2", "3"}
arr1 := arr[1:2]
arr1 = append(arr1, "asdf")
fmt.Println(arr)
fmt.Println(arr1)

// 切片删除元素,只能通过获取删除点前后的切片再合起来
var result = []string{"1", "2", "3", "4"}
// result[2:]...表示按result切片展开,等价于
// result[2], result[3]
result = append(result[0:1], result[2:]...)
fmt.Println(result)
fmt.Println(len(result))
fmt.Println(cap(result))

// 清空切片
var result2 = []string{"1", "2", "3"}
result2 = result2[0:0] // 将切片的开始和结束下标都设为0,即要清空
fmt.Println(result2)

map

GOLANG
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// 声明
var testMap map[keyType]valueType

// 初始化,最后一个键值要加一个"," 否则就需要把右括号放到最后一个键值后边
testMap = map[string]string {
"中国": "china",
"美国": "America",
}
// 使用make初始化,初始化可以不指定容量,但扩充时会损耗性能
CAPACITY := 2
testMap = make(map[string]string, CAPACITY)

// 遍历
for k := range testMap {
// 只遍历键
fmt.Println(k)
}
for _, v := range testMap {
// 只遍历值
fmt.Println(v)
}

// 删除键值对
delete(testMap, "中国")
fmt.Println(testMap)
// 如果想要清空map,只能重新创建一个map
testMap = map[string]string{}

// map值是任意类型可以用interface{}
testMap = map[string]interface{}{
"Code": 3,
"FromUserName": "username",
}

// map获取值时,返回的第二个结果可以表示是否获取到数据
match := map[string]struct{}{
"k": {},
}
result, ok1 := match["k"]
if ok1 {
fmt.Println("获取的数据为" + result)
} else {
fmt.Println("没有该键的数据")
}

json

这里展示 json 与 结构体 的映射

GOLANG
1
2
3
4
5
6
7
8
9
type Configuration struct {
// 键名 类型 对应的json的键
Key string `json:"key"`
}

// 实例化结构体
config := &Configuration{}
// 对结构体进行使用
config.Key = "helllo"

xml

这里展示 xml 与 结构体 的映射

GOLANG
1
2
3
type LoginInfo struct {
Ret int `xml:"ret"`
}

一些模块

常用函数

函数 相关说明
len() 获取数组长度
append() 给切片中添加元素。
使用方法可以 点击 查看

常用模块的解析

模块名 相关说明 跳转链接
bufio 用于流的处理 点击跳转
bytes 用作流和二进制数据处理 点击跳转
context 用于不同 goroutine 之间交换信息 点击跳转
errors 用作错误提示 点击跳转
exec 用于命令行的执行 点击跳转
filepath 文件路径相关处理 点击跳转
fmt 用于数据输出与格式化 点击跳转
html 用于 html 数据处理 点击跳转
http 用于 http 请求与响应处理 点击跳转
io 用于流的处理 点击跳转
json 用于 json 相关的处理 点击跳转
log 用于日志相关的输出 点击跳转
os 用于路径与文件相关的处理 点击跳转
regexp 用于正则的处理 点击跳转
runtime 用于操作系统相关信息获取
如系统类型
点击跳转
sort 排序相关 点击跳转
strconv 字符串与数字,bool等转换 点击跳转
strings 用于字符串相关处理 点击跳转
sync 并发处理模块 点击跳转
time 时间处理模块 点击跳转
unsafe 用于指针相关处理 点击跳转
url 用于 url 相关处理 点击跳转
xml 用于 xml 数据处理 点击跳转

bufio

GOLANG
1
2
// 用于读取文件
bufio.NewReader(file)

bytes

GOLANG
1
2
3
4
5
6
7
8
9
10
// 1. 创建一个缓冲区,然后给里边写数据
var c bytes.Buffer
c.WriteString(str1)
c.WriteString(str2)
fmt.Println(c.String())
// 2. 创建一个缓冲区,然后给里边读数据,之后就可以通过Bytes()方法使用数据了
c.ReadFrom(resp.Body)
fmt.Println(string(c.Bytes()))
// 3. 将字节数组直接缓冲到内存
result := bytes.NewBuffer(byteArr)

context

GOLANG
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Go语言中的 Context 模块可以用于在不同 goroutine 之间传递上下文信息,用于管理 goroutine 的生命周期。
// 它可以用于传递取消、超时、或者其他重要信息。
// 可以使用 Context.WithCancel 函数创建一个 context ,它将返回一个新的 context 和一个可以取消此 context 的函数。
// 也可以使用 Context.WithTimeout 函数创建一个 context ,它将在超时后取消 context 。
// 这些 context 可以传递给其他 goroutine ,以便它们可以取消因超时或者你的应用程序的取消而取消,或者在超时之前完成它们的工作。
// 创建一个可以取消的 context
ctx, cancel = context.WithCancel(context)
// 用于取消context
cancel()
// 用于创建一个空的 Context ,它可以用于在没有其他 Context 的情况下执行操作,也可以用于在其他 Context 的基础上创建新的 Context 。
context.Background()
// 用于检测 Context 是否已经被取消或超时。
// 它返回一个可以被读取的 channel ,当 Context 被取消或超时时,这个 channel 会收到一个值
// 所以 <-context.Done() 在未收到值之前会一直阻塞
context.Done()

errors

GOLANG
1
2
3
4
5
6
// 创建和一个新错误
var NetworkErr = errors.New("wechat network error")
// 用于判断是否为该错误
errors.Is(err, NetworkErr)
// 用来检查错误是否为指定的错误类型
errors.As(err, ret)

exec

GOLANG
1
2
// 执行命令行命令
exec.Command(cmd, args...).Start()

filepath

GOLANG
1
2
// 获取路径扩展名
filepath.Ext(name)

fmt

GOLANG
1
2
3
4
5
6
7
8
9
10
// 打印字符串
fmt.Println()
// 直接打印格式化字符串
fmt.Printf()
// 返回格式化的字符串
fmt.Sprintf(format string, a ...interface{})
// 将格式化的字符串赋值给一个变量
fmt.Fprintf(w io.Writer, format string, a ...interface{})
// 返回格式化的Error
fmt.Errorf()

html

GOLANG
1
2
// 将HTML实体&lt;转换为<这样的符号
html.UnescapeString()

http

GOLANG
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//================================
// 常量
http.MethodGet
//================================
// 常用方法
// 用于检测文件类型,文件类型可以读取文件前512字节来判断
http.DetectContentType(fileType)

// http请求
// 配置请求
req, err := http.NewRequest("POST", "https://www.teach-anything.com/api/generate", bytes.NewBuffer(requestData))
// 设置请求头
req.Header.Set("Content-Type", "application/json")
// 创建请求客户端
client := &http.Client{}
// 执行请求
response, err := client.Do(req)
// 关闭请求资源
defer response.Body.Close()
// 获取响应体字节流
body, err := ioutil.ReadAll(response.Body)

io

GOLANG
1
2
// 将后边的reader读到前边的writer
io.Copy(writer, resp.Body)

json

GOLANG
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 1 从文件解析json
// 将文件或Reader指针解析为json.Decoder
encoder := json.NewDecoder(f)
// 将json.Decoder解析为结构体
err = encoder.Decode(&config)

// 2 将结构体解析为json字节数组
requestData, err := json.Marshal(requestBody)

// 3. 将json字节数组解析为结构体
responseBody := &ResponseBody{}
err = json.Unmarshal(body, responseBody)

// 4.1 将结构体编码为字节数组存到内存某个缓冲区
var buffer bytes.Buffer
encoder := json.NewEncoder(&buffer)
// 这里要设置禁止html转义
encoder.SetEscapeHTML(false)
err := encoder.Encode(structInfo)
// 4.2 将结构体编码为字节数组存到内存某个缓冲区
data, _ := json.Marshal(structInfo)
body := bytes.NewBuffer(data)

log

GOLANG
1
2
3
4
5
// 进行日志操作
// 打印错误日志
log.Fatalf("open config err: %v", err)
// 普通打印
log.Printf("Received Group %v Text Msg : %v", group.NickName, msg.Content)

os

GOLANG
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 操作系统模块

// 用于打开文件
f, err := os.Open("文件名")
// 读取环境变量
ApiKey := os.Getenv("ApiKey")
// 创建临时文件,第一个参数为文件夹路径,可以为空,第二个为文件名称模式,
// 有*则随机生成的字符串会替换最后一个*
os.CreateTemp("/root/work/go-project/wechatbot/openwechat", "*.*")
// 删除文件
os.Remove()
// 用于获取文件的信息,如文件名,大小,是否为目录等
os.FileInfo
// 用于文件的各种操作
os.File
// 将文件中的数据读取到字节数组中,游标会变化
file.Read(arr)
// 将字节数组的数据写到文件
file.Write(arr)
// 读取文件后,游标进行了变化,如果需要重头读取文件,则可以重置游标
// 第一个参数是移动几个字节,第二个参数是从哪个位置开始偏移;0代表从文件开头开始算起,1代表从当前位置开始算起,2代表从文件末尾算起
file.Seek(0, 0)
// 用于截断文件,多余的部分会被丢弃
file.Truncate(0)
// 获取文件名
file.Name()

regexp

GOLANG
1
2
3
4
5
6
7
8
// 正则的编译
uuidRegexp := regexp.MustCompile(`uuid = "(.*?)";`)
// 用正则从字符串的字节数组中找到匹配的数据
results := uuidRegexp.FindSubmatch(buffer.Bytes())
match = string(results[1])
// 寻找字符串中的所有匹配组,-1的位置表示寻找的次数,这里表示匹配所有
// 如正则为a(x*)b,则-axxb-ab- =》 [["axxb" "xx"] ["ab" ""]]
results := uuidRegexp.FindAllStringSubmatch("-axxb-ab-",-1)

runtime

GOLANG
1
2
// 获取操作系统类型,如windows,darwin,"linux", "freebsd", "openbsd", "netbsd"等
runtime.GOOS

sort

GOLANG
1
2
// 升序排序
sort.Sort()

strconv

GOLANG
1
2
3
4
5
6
// 以十进制格式化uin这个数字到字符串
strconv.FormatInt(uin, 10)
// 以十进制转换字符串为数字,二进制为不超过8位
strconv.Parse(str, 10, 8)
// 科学计数法表示10^6
1e6

strings

GOLANG
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 字符串相关处理
// 字符串替换
strings.ReplaceAll(str, oldStr, newStr)
// 替换字符串。最后的-1表示替换所有情况
strings.Replace(text, old, new, -1)
// 字符串去除空格
strings.TrimSpace(str)
// 字符串去除某字符串
strings.Trim(str, "\n")
// 将字符串数组组成字符串
strings.Join(strArr, "|")
// 去掉前缀:.asdf =》 asdf
strings.TrimPrefix(strArr, ".")
// 获取子串下标,没找到返回-1
strings.Index(str, subStr)

sync

GOLANG
1
2
3
4
5
6
// 使用sync模块可以用于并发处理
// Sync.Once是一个Go语言中的结构,用于确保函数只执行一次。
// 它可以用于在程序初始化时调用特定函数,以确保只有一个goroutine可以在某个特定的时间段内执行该函数。
// 它也可以用于防止多个goroutine同时执行特定函数,从而防止多个goroutine同时修改特定变量。
// 调用sync.Once.Do()执行函数,可以用go关键字开启一个新的协程执行代码
sync.Once

time

GOLANG
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 获取分钟的时间表示
time.Minute
// 将普通的Unix时间戳转为go语言的时间类型
time.Unix()
// 用于格式化时间
now.Format("2006-01-02 15:04:05")
// 获取从指定时间到现在的时间
time.Since(start)
// 获取当前时间
time.Now()
// 创建一个定时器,它可以在指定的时间间隔内发送一个信号,以便在指定的时间间隔内执行某些操作。
time.NewTicker()
// 返回一个通道,每隔一段时间就给通道发一些数据,可用于switch
ticker.C
for {
select {
case <-ticker.C:
// 每隔一段时间, 将数据同步到storage中
if err := h.bot.DumpHotReloadStorage(); err != nil {
return err
}
case <-h.bot.Context().Done():
// 当Bot关闭的时候, 退出循环
return nil
}
}

unsafe

GOLANG
1
2
3
4
5
6
7
8
9
10
// 1. 用于转换指针类型
unsafe.Pointer()
// 例:
tt := []byte{48}
ttPtr := &tt // 字节数组地址
result := (*string)(unsafe.Pointer(ttPtr)) // 将字节数组指针转为字符串指针
fmt.Println(*result == "0") // 通过*获取字符串指针的值,然后与"0"比较

// 2. 返回变量占用字节数
unsafe.Sizeof(nunber)

url

GOLANG
1
2
3
4
5
6
7
8
9
10
11
// 解析url字符串到URL结构,方便操作URL的各个部分
url.Parse()
// 将查询参数编码为url.Values{}
url.ParseQuery()
// 用于创建一个map,存储url的键值参数
// 如果直接创建,值需要加{},url.Values{"mod": {"desktop"}}
url.Values{}
// 用于将map编码为a=1&b=2的字符串形式
(url.Values).Encode()
// 将URL结构体转换为字符串
(*url.URL).String()

xml

GOLANG
1
2
// 1. 将结构体解析为xml字节数组
xml.Marshal()

相关总结

一些函数

  • bytes.Bufferio.Reader

bytes.Buffer就是内存的字节数组,io.Reader读取的也是字节数组

  • structmap的区别

结构体定义了键与类型,map可以放任意的键

  • readerwriter的理解(暂时)

reader读取流(字节流,字符流),将流转成需要的格式;writer写出流(字节流,字符流),将数据转成流写出

函数回调

GOLANG
1
2
3
4
5
6
// 在结构体中加入一个函数类型的字段,则当用户创建了这个对象之后,需要对某个功能进行自定义处理
// 那么就可以将用户的函数交给这个对象的这个属性,在需要的时候进行调用
// 字段名一般可以作为功能的表示
type Bot struct {
ScanCallBack func(body CheckLoginResponse) // 扫码回调,可获取扫码用户的头像
}
 评论