go语言包-02
go语言big包
实际开发中,对于超出 int64 或者 uint64 类型的大数进行计算时,如果对精度没有要求,使用 float32 或者 float64 就可以胜任,但如果对精度有严格要求的时候,我们就不能使用浮点数了,因为浮点数在内存中只能被近似的表示。
Go语言中 math/big 包实现了大数字的多精度计算,支持 Int(有符号整数)、Rat(有理数)和 Float(浮点数)等数字类型。
这些类型可以实现任意位数的数字,只要内存足够大,但缺点是需要更大的内存和处理开销,这使得它们使用起来要比内置的数字类型慢很多。
基本使用案例:
func main() {
// 声明bigInt方法1
pNum := big.NewInt(8234567891234567899)
// 方法2
pNum2 := new(big.Int)
pNum2.SetInt64(8234567891234567899)
// bigInt相加
pNum3 := pNum.Add(pNum, pNum2)
fmt.Println(pNum3)
// 将*big.Int转换为uint64类型
fmt.Println(pNum3.Uint64())
}
除了上述的 Set 函数,math/big 包中还提供了一个 SetString() 函数,可以指定进制数,比如二进制、十进制或者十六进制等!
将字符串转换为*big.Int类型
func main() {
pNum := new(big.Int)
pNum2, ok := pNum.SetString("12345678912345678912123456789", 10)
fmt.Println(pNum2, ok, reflect.TypeOf(pNum2)) // 12345678912345678912123456789 true *big.Int
}
计算第1000位的斐波那契数列:
const LIM = 1000
var fibs = make([]*big.Int, LIM)
func main() {
start := time.Now()
for i := 0; i < LIM; i++ {
result := fibonacci(int64(i))
fmt.Println(i+1, ":", result)
}
fmt.Println("用时:", time.Now().Sub(start))
}
func fibonacci(i int64) (res *big.Int) {
if i <= 1 {
res = big.NewInt(i)
}else{
temp := new(big.Int)
res = temp.Add(fibs[i-1], fibs[i-2])
}
fibs[i] = res
return
}
执行结果:
997 : 6341685300418834712936873743652479702279493077782703784593930186219165735154458712792650716708440646016361617676315200611489358319
848695671037772054859840711063922357900430130265783715452104982240098275933872
998 : 1026106236203326233660492672924522213266855812060212427776462290569940798254671148827285946888745795908773311924256407785074365766
1180827326798539177758919828135114407499369796465649524266755391104990099120377
999 : 1660274766245209704954180047289770183494805119838482806235855309191857371770117020106551018559589860510409473691887927846223301598
1029522997836311232618760539199036765399799926731433239718860373345088375054249
1000 : 268638100244853593861467272021429239676166093189869523401231759976179817002478816893383696544833565641918278561614433563129766736
42210350324634850410377680367334151172899169723197082763985615764450078474174626
用时: 287.2678ms
go语言正则表达式:regexp包
正则表达式是一种进行模式匹配和文本操纵的复杂而又强大的工具。虽然正则表达式比纯粹的文本匹配效率低,但是它却更灵活,按照它的语法规则,根据需求构造出的正则表达式能够从原始文本中筛选出几乎任何你想要得到的字符组合。
Go语言通过 regexp 包为正则表达式提供了官方支持,其采用 RE2 语法,除了\c、\C外,Go语言和 Perl、Python 等语言的正则基本一致。
正则表达式语法规则
正则表达式是由普通字符(例如字符 a 到 z)以及特殊字符(称为”元字符”)构成的文字序列,可以是单个的字符、字符集合、字符范围、字符间的选择或者所有这些组件的任意组合。
下面的表格中列举了构成正则表达式的一些语法规则及其含义。
- 字符
语法 | 说明 | 表达式示例 | 匹配结果 |
---|---|---|---|
一般字符 | 匹配自身 | abc | abc |
. | 匹配任意除换行符”\n”外的字符, 在 DOTALL 模式中也能匹配换行符 | a.c | abc |
\d | 数字:[0-9] | a\dc | a1c |
\D | 非数字:[^\d] | a\Dc | abc |
\s | 空白字符:[<空格>\t\r\n\f\v] | a\sc | a c |
\S | 非空白字符:[^\s] | a\Sc | abc |
\w | 单词字符:[A-Za-z0-9] | a\wc | abc |
\W | 非单词字符:[^\w] | a\Wc | a c |
- 数量词,用在字符后
语法 | 说明 | 表达式示例 | 匹配结果 |
---|---|---|---|
* | 匹配前一个字符 0 或无限次 | abc* | ab 或 abccc |
+ | 匹配前一个字符 1 次或无限次 | abc+ | abc 或 abccc |
? | 匹配前一个字符 0 次或 1 次 | abc? | ab 或 abc |
匹配前一个字符 m 次 | ab{2}c | abbc | |
匹配前一个字符 m 至 n 次,m 和 n 可以省略,若省略 m,则匹配 0 至 n 次;若省略 n,则匹配 m 至无限次 | ab{1,2}c | abc 或 abbc |
- 边界匹配
语法 | 说明 | 表达式示例 | 匹配结果 |
---|---|---|---|
^ | 匹配字符串开头,在多行模式中匹配每一行的开头 | ^abc | abc |
$ | 匹配字符串末尾,在多行模式中匹配每一行的末尾 | abc$ | abc |
\A | 仅匹配字符串开头 | \Aabc | abc |
\Z | 仅匹配字符串末尾 | abc\Z | abc |
\b | 匹配 \w 和 \W 之间 | a\b!bc | a!bc |
\B | [^\b] | a\Bbc | abc |
- 特殊构造(不作为分组)
语法 | 说明 | 表达式示例 | 匹配结果 |
---|---|---|---|
(?:…) | (…) 的不分组版本,用于使用 “ | “ 或后接数量词 (?:abc) | abcabc |
(?iLmsux) | iLmsux 中的每个字符代表一种匹配模式,只能用在正则表达式的开头,可选多个 | (?i)abc | AbC |
(?#…) | # 后的内容将作为注释被忽略。 | abc(?#comment)123 | abc123 |
(?=…) | 之后的字符串内容需要匹配表达式才能成功匹配 | a(?=\d) | 后面是数字的 a |
(?!…) | 之后的字符串内容需要不匹配表达式才能成功匹配 | a(?!\d) | 后面不是数字的 a |
(?<=…) | 之前的字符串内容需要匹配表达式才能成功匹配 | (?<=\d)a | 前面是数字的a |
(?<!…) | 之前的字符串内容需要不匹配表达式才能成功匹配 | (?<!\d)a | 前面不是数字的a |
regexp包的使用
- 匹配指定类型的字符串
func main() {
buf := "abc azc a7c aac 888 a9c tac"
// 解析正则表达式,如果成功返回解析器
reg1 := regexp.MustCompile("a.c")
if reg1 == nil {
fmt.Println("regexp err")
return
}
// 根据规则提取关键信息
ret := reg1.FindAllString(buf, -1)
fmt.Println(ret)
}
- 匹配a和c中间包含一个数字的字符串
func main() {
buf := "abc azc a7c aac 888 a9c tac"
// 解析正则表达式,如果成功返回解析器
//reg1 := regexp.MustCompile(`a[0-9]c`)
reg1 := regexp.MustCompile(`a\dc`)
if reg1 == nil {
fmt.Println("regexp err")
return
}
// 根据规则提取关键信息
ret := reg1.FindAllString(buf, -1)
fmt.Println(ret)
}
- 匹配字符串中的小数
func main() {
buf := "43.14 567 agsdg 1.23 7. 8.9 1sdljgl 6.66 7.8 "
// 解析正则表达式,如果成功获取解析器
reg1 := regexp.MustCompile(`\d+\.\d+`)
if reg1 == nil {
fmt.Println("regexp err")
return
}
// 提取关键信息
ret := reg1.FindAllString(buf, -1)
fmt.Println(ret)
}
- 匹配div标签中的内容
func main() {
buf := `
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<title>Go语言中文社区 | Go语言入门教程</title>
</head>
<body>
<div>Go语言简介</div>
<div>Go语言基本语法
Go语言变量的声明
Go语言教程简明版
</div>
<div>Go语言容器</div>
<div>Go语言函数</div>
</body>
</html>
`
// 解析正则表达式,如果成功返回解析器
reg1 := regexp.MustCompile(`<div>(?s:(.*?))</div>`)
if reg1 == nil {
fmt.Println("regexp err")
return
}
// 提取关键信息
ret := reg1.FindAllStringSubmatch(buf, -1)
// 过滤标签
for _, tmp := range ret {
fmt.Println(tmp[1])
}
}
- 通过Compile方法返回一个Regexp对象,实现匹配、查找、替换相关的功能
func main() {
// 目标字符串
searchIn := "John: 2578.34 William: 4567.23 Steve: 5632.18"
pat := `\d+.\d+`
if ok, _ := regexp.Match(pat, []byte(searchIn)); ok {
fmt.Println("Match Found。")
}
// 将匹配的部分替换为##.#
re, _ := regexp.Compile(pat)
ret := re.ReplaceAllString(searchIn, "##.#")
fmt.Println(ret)
// 正则表达式
myFunc := func(s string) string {
v, _ := strconv.ParseFloat(s, 32)
return strconv.FormatFloat(v*2, 'f', 2, 32)
}
// 参数为函数时
ret = re.ReplaceAllStringFunc(searchIn, myFunc)
fmt.Println(ret)
}
输出结果:
Match Found。
John: ##.# William: ##.# Steve: ##.#
John: 5156.68 William: 9134.46 Steve: 11264.36
上面代码中Compile方法可以解析并返回一个正则表达式,如果成功返回,则说明该正则表达式正确可用于匹配文本,另外我们也可以使用 MustCompile 方法,它也可以像 Compile 方法一样检验正则的有效性,但是当正则不合法时程序将 panic。
go语言time包:时间和日期
时间和日期是我们开发中经常会用到的,Go语言中的 time 包提供了时间显示和测量等所用的函数,本节我们就来介绍一下 time 包的基本用法。
time包简介
时间一般包含时间值和时区,可以从go语言中time包的源码中看出
type Time struct {
wall uint64
ext int64
loc *Location
}
上面代码中:
- wall: 表示举例公元1年1月1日 00:00:00UTC的描述
- ext: 表示纳秒
- loc: 代表时区,主要处理偏移量,不同的时区,对应的时间不一样。
如何正确表示时间呢?
公认最准确的计算应该是使用“原子震荡周期”所计算的物理时钟了(Atomic Clock, 也被称为原子钟),这也被定义为标准时间(International Atomic Time)。
而我们常常看见的 UTC(Universal Time Coordinated,世界协调时间)就是利用这种 Atomic Clock 为基准所定义出来的正确时间。UTC 标准时间是以 GMT(Greenwich Mean Time,格林尼治时间)这个时区为主,所以本地时间与 UTC 时间的时差就是本地时间与 GMT 时间的时差。
UTC + 时区差 = 本地时间
国内一般使用的是北京时间,与UTC时间关系如下:
UTC+8个小时=北京时间
在go语言的time包里面有两个时区变量,如下:
time.UTC: UTC时间
time.Local:本地时间
时间的获取
- 获取当前时间
我们可以通过time.Now()函数来获取当前的时间对象,然后通过事件对象来获取当前的时间信息。示例代码如下:
func main() {
now := time.Now()
fmt.Println(now)
year, month, day := now.Year(), now.Month(), now.Day()
hour, minute, second := now.Hour(), now.Minute(), now.Second()
fmt.Printf("%d-%02d-%02d %02d:%02d:%02d\n", year, month, day, hour, minute, second)
}
- 获取时间戳
时间戳是自 1970 年 1 月 1 日(08:00:00GMT)至当前时间的总毫秒数,它也被称为 Unix 时间戳(UnixTimestamp)。
基于时间对象获取时间戳的示例代码如下:
func main() {
now := time.Now()
fmt.Println(now.Unix()) // 时间戳秒
fmt.Println(now.UnixNano()) // 纳秒级时间戳
}
使用time.Unix()函数可以将时间戳转换为时间格式:
func main() {
now := time.Now()
timestamp := now.Unix() // 获取时间戳
fmt.Println(timestamp)
currentTime := time.Unix(timestamp, 0) // 将秒级时间戳转换为当前时间
fmt.Println(currentTime)
}
- 获取当前是星期几
time 包中的 Weekday 函数能够返回某个时间点所对应是一周中的周几,示例代码如下:
func main() {
now := time.Now()
fmt.Println(now.Weekday().String()) // Thursday
}
时间操作函数
- Add
func main() {
now := time.Now()
ret := now.Add(time.Hour) // 一个小时之后的时间
fmt.Println(ret) // 2022-08-25 15:11:23.1365266 +0800 CST m=+3600.010838701
}
如果要求一个小时之前的时间,可以传递负值即可。
2. Sub
求两个时间之间的差值
func main() {
now := time.Now()
later := now.Add(time.Hour * 2)
ret := later.Sub(now) // Sub计算两个时间的差
fmt.Println(ret) // 2h0m0s
}
- Equal
判断两个时间是否相等
func main() {
ret1 := time.Date(2022, time.Month(8), 25, 14, 23, 59, 0, time.Local)
ret2 := time.Date(2022, time.Month(8), 25, 6, 23, 59, 0, time.UTC)
fmt.Println(ret1.Equal(ret2)) // true
}
Equal 函数会考虑时区的影响,因此不同时区标准的时间也可以正确比较,Equal 方法和用 t==u 不同,Equal 方法还会比较地点和时区信息。
4. Before
判断一个时间点是否在另一个时间点之前,不同的时区也可以正常比较
func main() {
ret1 := time.Date(2022, time.Month(8), 25, 14, 23, 59, 0, time.Local)
ret2 := time.Date(2022, time.Month(8), 25, 6, 23, 57, 0, time.UTC)
fmt.Println(ret2.Before(ret1)) // true
}
- After
判断一个时间点是否在另一个时间点之后
func main() {
ret1 := time.Date(2022, time.Month(8), 25, 14, 23, 59, 0, time.Local)
ret2 := time.Date(2022, time.Month(8), 25, 6, 23, 59, 1, time.UTC)
fmt.Println(ret2.After(ret1)) // true
}
定时器
使用time.Tick(时间间隔)可以设置定时器,定时器的本质上就是一个通道(channel),示例如下:
func main() {
ticker := time.Tick(time.Second)
for i := range ticker {
fmt.Println(i) // 每秒钟执行一次
}
}
时间格式化
时间类型有一个自带的format方法进行格式化,需要注意的是Go语言中格式化时间模板不是常见的Y-m-d H:M:S 而是使用Go语言的诞生时间 2006 年 1 月 2 号 15 点 04 分 05 秒。
提示:如果想将时间格式化为 12 小时格式,需指定 PM。
func main() {
now := time.Now()
fmt.Println(now) // 2022-08-25 16:08:33.715708 +0800 CST m=+0.012186301
fmt.Println(now.Format("2006-01-02 15:04:05.000 Mon Jan")) // 2022-08-25 16:13:20.577 Thu Aug
fmt.Println(now.Format("2006-01-02 03:04:05 PM Mon Jan")) // 2022-08-25 04:13:20 PM Thu Aug
fmt.Println(now.Format("2006/01/02 15:04")) // 2022/08/25 16:14
fmt.Println(now.Format("15:04 2006/01/02")) // 16:14 2022/08/25
fmt.Println(now.Format("2006/01/02")) // 2022/08/25
}
运行结果:
2022-08-25 16:15:14.9331708 +0800 CST m=+0.008616501
2022-08-25 16:15:14.933 Thu Aug
2022-08-25 04:15:14 PM Thu Aug
2022/08/25 16:15
16:15 2022/08/25
2022/08/25
解析字符串格式的时间
Parse函数可以解析一个格式化的时间字符串并返回它代表的时间
func main() {
layout := "2006-01-02 15:04:05"
timeStr := "2022-08-25 16:20:59"
ret, err := time.Parse(layout, timeStr)
fmt.Println(ret, err, reflect.TypeOf(ret)) // 2022-08-25 16:20:59 +0000 UTC <nil> time.Time
ret, err = time.ParseInLocation(layout, timeStr, time.Local)
fmt.Println(ret, err, reflect.TypeOf(ret)) // 2022-08-25 16:20:59 +0800 CST <nil> time.Time
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析