Go 进阶指南

发布于 2022-01-11 13:05:58 字数 7515 浏览 1145 评论 0

对于编程语言来说,可以简单地分为基础和高级用法两部分,基础用法主要在于自己多写,只要多写,慢慢就会熟练了,也没有看别人总结的必要。所以这里准备总结一下进阶用法。

看完以上的入门内容之后,就可以自己写一些简单的程序了,这个时候可以再去看《The Go Programming Language》这本书,被称为“Go语言圣经”,书并不厚,内容比较基础但覆盖的比较全面。中文版的民间翻译也不错:Go 语言圣经(中文版)

之后可以沿着下面几个方向来进阶学习:

  1. 并发编程专题:Go 语言圣经中也有,也比较推荐 Go 语言高级编程 中第一章的并发部分
  2. 性能优化专项:可以多看一些博客文章,以及推荐 Go 语言高性能编程
  3. Go 底层原理,包括并发(sync 包);slice 与 map 的底层实现;内存管理、垃圾回收;Goroutine,调度器
  4. Go Web编程:如果是为了开发工作的话不看也行,而看的目的就说要上升到源码,这一步开始就可以追求源码级了解了。参考的电子书:Go 语言高级编程
  5. Go 著名项目,源码分析:比如消息队列、grpc、gin、分布式缓存 groupcache,也可以参考 7days-golang

Useful Go Built-in Libraries

当然,仅仅掌握最基本的用法,在开发中还是会遇到很多不顺手的地方,很多地方在实现的时候可能还是需要现场去查怎么用。但掌握了 go 的一些常用内置库的用法之后,开发起来就能顺手多了。下面介绍一些高频的内置库

推荐教程:Go 语言标准库

time

获取时间、时间戳

// 获取当前时间
timeNow := time.Now()
// .Unix()可以将time.Time转换为时间戳
timestampNow := time.Now().Unix()

时间的加减

time.Now().AddDate(0,0,1) // 当前时间+1天
time.Now().AddDate(0,-1,0) // 当前时间-1个月
time.Now().Add(3*time.Hour) // 当前时间+3小时

interval, _ := time.ParseDuration("24h")
interval1, _ := time.ParseDuration(fmt.Sprintf("-%vh", 3*24))
temp := time.Now().Add(interval)

// 得到某个时间距当前时间的差值
var a time.Time
duration := time.Since(a).Hours()

字符串格式的时间、时间、时间戳互相转换

timeNow := time.Now()
// 时间转时间戳:.Unix()
nowUnix := timeNow.Unix()
// 时间戳转时间: time.Unix()
timeNow = time.Unix(nowUnix, 0)
// 时间转字符串格式,格式为"2006-01-02 15:04:05",可以控制输出的格式,例如"2006-01 15:04"
nowStr := timeNow.Format("2006-01-02 15:04:05")
// 字符串格式解析为本地时间
timeNow, err := time.ParseInLocation("2006-01-02 15:04:05", nowStr, time.Local)

获取周几、本月1号的时间

// 获取周几
weekDay := time.Weekday()
time.Weekday() == time.Monday() // 判断是否为周一

// 本月1号
timeNow := time.Now
firstOfMonth := time.Date(timeNow.Year(), timeNow.Month(), 1, 0, 0, 0, 0, time.Local)

encoding / json

需要事先定义好变量类型,可以是普通数据结构,也可以是 struct

a := map[string]interface{}
aJosn, err := json.Marshal(a) // 编码为json

err := json.Unmarshall(aJson, &a) // json解码

net/http

Get 方法

resp, err := http.Get("https://www.baidu.com")
if err != nil {return}
defer resp.Body.Close()  // 一定要关闭Body
s, err := ioutil.ReadAll(resp.Body)

Post 方法,主要使用 http.Client 的 Do 方法

httpClient := &http.Client{
  Timeout: 10 * time.Second,  // 一定要注意设置超时时间
}
// 假设已经设置好了url和要post的表单数据,这里FormData是json编码之后的数据,json编码见上面
url := "...."
FormData := "...."
// 设置request参数
req, err := http.NewRequest("POST", url, bytes.NewBuffer(FormData))
req.Header.Set("Content-Type", "application/json") // 还可以设置请求头参数
// 初始化http.Client发送请求
resp, err := httpClient.Do(req)
if err != nil {return}
defer resp.Body.Close()

// 解析返回的数据
statusCode := resp.StatusCode
header := resp.Header
body, _ := ioutil.ReadAll(resp.Body)

math / rand

// 先生成随机数种子
rand.Seed(time.Now().UnixNano())
rand.Intn(x) // 在0-x的范围内随机选一个数

go 中的 rand 不支持从一个 slice 中随机选择一个元素,因此想要实现这个功能,只能结合:

s1 := []string{"1","2","3"}
rand_value := s1[rand.Intn(len(s1))]

rand 还可以打乱数组中的元素,调用rand.Shuffle即可:

rand.Seed(time.Now().UnixNano())
// 将lst乱序,第一个参数是lst长度,第二个参数是一个func,实现了swap功能
rand.Shuffle(len(lst), func(i, j int) {lst[i], lst[j] = lst[j], lst[i]})

os

获取命令行参数

if len(os.Args) > 1 {
  // os.Args[0]是命令本身的名字,从索引1开始才是命令行参数
  a := os.Args[1]
}

strings

一些字符串操作

import "strings"

a := "go"
b := "hello"
strings.EqualFold(a, b)  // 计算两个字符串忽略大小写是否相等
strings.Contains(a, "i")
strings.ContainsAny(a, "i x")
strings.Count(b, "lo")
strings.Split("hello,world", ",")  // 同python的split
strings.Join([]string{"1","2","3"}, ",") // 同python的join
strings.HasPrefix("http://www", "http")
strings.HasSuffix("hello.go", "go")
strings.Index("hello", "l")
strings.LastIndex("hello","l")
strings.Join([]string{"name=xxx", "age=xx"}, "&")
strings.Replace("this\nis\na\nhello\n", "\n", " ", 2)  // 最后一个int指替换次数,如果小于0表示全部替换
strings.ToLower(a)  // strings.ToUpper(s string)
strings.Trim(str, " ") // 基本上就是python中的strip

strconv

常用的字符串转换,主要是字符串和整数互转

主要使用 strconv.Parseint(s string, base int, bitSize int),返回 int64,参数 base 表示转换为指定的进制,比如 10 进制,bitSize 表示整数的实际类型,用于确定取值范围,比如 0、8、16、32 和 64 分别代表 int、int8、int16、int32 和 int64。:

import "strconv"
var a = "43212"
a_conv, _ := strconv.Parseint(a, 10, 0)

int64 转换为字符串

strconv.FormatInt(102323, 10) // 10是base

unicode

判断字符串是否含中文:

import "unicode"
str := "Hello, 世界"
for _, r := range str {
  if unicode.Is(unicode.Han, r) {
    fmt.Print(string(r))
  }
}
// 直接取len()的话,一个中文字符会占3个长度

判断中文字符串长度:

import "unicode/utf8"
utf8.RuneCountInString(str) // 判断长度
// 截取中文字符串
str = string([]rune(str)[:4])

sort

sort 包使得我们可以对自定义的一些类型进行排序,而我们需要做的就是对我们想要排序的类型实现 Len()Less()Swap() 三个方法,之后就可以调用 sort.Sort() 进行排序。比如,我们自定义了一种结构体:

type Person struct {
  Name string
  Age int
}

那么,当我们相对一个 []Person 类型的变量进行排序的时候,我们首先需要对 []Person 这种类型实现一下 Len()Less()Swap() 三个方法:

type PersonSlice []Person

func(p *PersonSlice) Len() int {
  return len(p)
}
func (p *PersonSlice) Swap(i, j int) {
  p[i], p[j] = p[j], p[i]
}
func (p *PersonSlice) Less(i, j int) bool {
  return p[i].age < p[j].age  // 如果要从高到低排则交换一下顺序即可
}

接下来就可以直接调用 sort 包进行排序了:

var ps []Person // 这里省略初始化

sort.Sort(PersonSlice(ps)) // 先进行类型转换,将ps转换为PersonSlice类型

当然,对于一些基础的类型:intfloat64string,sort 包已经内置了对它们的排序函数,直接调用 sort.Strings() 就行

testing

简单介绍一下 golang 中的测试函数,使用测试函数我们可以方便地测试代码,比如我们在某个包中实现了一些函数,想要测试函数的功能是否符合预期,这时专门为这些函数去写一个main函数来执行显得有些麻烦,而通过测试函数我们可以实现这一目的。

在包目录内,新建一个以 _test.go 结尾的文件,然后把测试代码放在这个文件里面。在构建代码的时候,*_test.go 不会被构建为包的一部分。

*_test.go 文件中,我们可以开始写我们的测试函数,测试函数需要以 Test 开头,并且需要使用testing包,函数的后缀名需要以大写开头,比如:

fun TestName(t *testing.T) {
  /*
  ...
  */
}

写好测试函数之后,我们在包目录下,运行 go test,然后包下面的所有测试函数都会得到调用,并且输出结果

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

JSmiles

生命进入颠沛而奔忙的本质状态,并将以不断告别和相遇的陈旧方式继续下去。

0 文章
0 评论
84961 人气
更多

推荐作者

醉城メ夜风

文章 0 评论 0

远昼

文章 0 评论 0

平生欢

文章 0 评论 0

微凉

文章 0 评论 0

Honwey

文章 0 评论 0

qq_ikhFfg

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文