本教程全面涵盖了Go语言基础的各个方面。一共80个例子,每个例子对应一个语言特性点,既适合新人快速上手,也适合工作中遇到问题速查知识点。
教程代码示例来自go by example,文字部分来自本人自己的理解。
本文是教程系列的第三部分,共计20个例子、约1.5万字。
系列文章快速跳转:
跟着实例学Go语言(一)
跟着实例学Go语言(二)
跟着实例学Go语言(三)
跟着实例学Go语言(四)
目录
- 41. Mutexes
- 42. Stateful Goroutines
- 43. Sorting
- 44. Sorting by Functions
- 45. Panic
- 46. Defer
- 47. Recover
- 48. String Functions
- 49. String Formatting
- 50. Text Templates
- 51. Regular Expressions
- 52. JSON
- 53. XML
- 54. Time
- 55. Epoch
- 56. Time Formatting / Parsing
- 57. Random Numbers
- 58. Number Parsing
- 59. URL Parsing
- 60. SHA256 Hashes
41. Mutexes
Go中处理同步的另一方式是使用互斥锁,这也是许多其他语言会采用的方式,保证加锁的代码块为原子操作。
package main
import (
"fmt"
"sync"
)
// 封装结构,包括:互斥锁和它保护的map计数器
type Container struct {
mu sync.Mutex
counters map[string]int
}
func (c *Container) inc(name string) {
// 通过互斥锁保证对计数器的修改是原子操作
c.mu.Lock()
defer c.mu.Unlock()
c.counters[name]++
}
func main() {
c := Container{
counters: map[string]int{"a": 0, "b": 0},
}
var wg sync.WaitGroup
doIncrement := func(name string, n int) {
for i := 0; i < n; i++ {
c.inc(name)
}
wg.Done()
}
wg.Add(3)
go doIncrement("a", 10000)
go doIncrement("a", 10000)
go doIncrement("b", 10000)
wg.Wait()
fmt.Println(c.counters)
}
$ go run mutexes.go
map[a:20000 b:10000]
42. Stateful Goroutines
比起互斥锁,Go更推荐使用通道通信的方式来实现goroutine之间的同步。以下是一个使用channel通信来实现并发安全的缓存服务的例子。缓存服务从reads和writes两个channel中读取读写命令,并依次执行,从而避免了多个goroutine对缓存数据的竞争。两个channel在这里起到了消息队列的作用。
package main
import (
"fmt"
"math/rand"
"sync/atomic"
"time"
)
type readOp struct {
key int
resp chan int
}
type writeOp struct {
key int
val int
resp chan bool
}
func main() {
var readOps uint64
var writeOps uint64
reads := make(chan readOp)
writes := make(chan writeOp)
go func() {
var state = make(map[int]int)
for {
// 缓存服务从两个channel取命令,串行执行,每次一条
select {
case read := <-reads:
read.resp <- state[read.key]
case write := <-writes:
state[write.key] = write.val
write.resp <- true
}
}
}()
for r := 0; r < 100; r++ {
go func() {
for {
read := readOp{
key: rand.Intn(5),
resp: make(chan int)}
reads <- read
<-read.resp
// 接收到返回后将计数加1
atomic.AddUint64(&readOps, 1)
time.Sleep(time.Millisecond)
}
}()
}
for w := 0; w < 10; w++ {
go func() {
for {
write := writeOp{
key: rand.Intn(5),
val: rand.Intn(100),
resp: make(chan bool)}
writes <- write
<-write.resp
atomic.AddUint64(&writeOps, 1)
time.Sleep(time.Millisecond)
}
}()
}
time.Sleep(time.Second)
// 用LoadUint64保证读取计数为原子操作
readOpsFinal := atomic.LoadUint64(&readOps)
fmt.Println("readOps:", readOpsFinal)
writeOpsFinal := atomic.LoadUint64(&writeOps)
fmt.Println("writeOps:", writeOpsFinal)
}
$ go run stateful-goroutines.go
readOps: 71708
writeOps: 7177
43. Sorting
Go内置的排序函数支持对不同数据类型slice的排序。
package main
import (
"fmt"
"sort"
)
func main() {
// 排序string和int要用不同的函数
strs := []string{"c", "a", "b"}
sort.Strings(strs)
fmt.Println("Strings:", strs)
ints := []int{7, 2, 4}
sort.Ints(ints)
fmt.Println("Ints: ", ints)
// 判断是否已排序
s := sort.IntsAreSorted(ints)
fmt.Println("Sorted: ", s)
}
$ go run sorting.go
Strings: [a b c]
Ints: [2 4 7]
Sorted: true
44. Sorting by Functions
除了按自然序排序,我们也可以通过函数自定义排序规则,对任意类型的结构体进行排序。下面例子是自定义byLength类型的结构体,然后对其中的string按长度进行排序。
package main
import (
"fmt"
"sort"
)
// 新类型是string slice的别名
type byLength []string
// 实现了排序所需的三个方法:Len、Swap、Less
// byLength实际就是string slice,是引用类型,因此不用传指针
func (s byLength) Len() int {
return len(s)
}
func (s byLength) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s byLength) Less(i, j int) bool {
return len(s[i]) < len(s[j])
}
func main() {
fruits := []string{"peach", "banana", "kiwi"}
// byLength实现排序接口后,可执行被Sort调用
sort.Sort(byLength(fruits))
fmt.Println(fruits)
}
$ go run sorting-by-functions.go
[kiwi peach banana]
45. Panic
panic用于表示预期外的严重错误,遇到这种错误时应该第一时间失败,中止程序运行。对于一般的错误处理,Go推荐尽量使用error而非panic。
package main
import "os"
func main() {
panic("a problem")
_, err := os.Create("/tmp/file")
if err != nil {
// 当遇到error不知道怎么处理时,可以包装成panic
panic(err)
}
}
$ go run panic.go
panic: a problem
// 程序会panic的那行直接退出,退出码不再是0
goroutine 1 [running]:
main.main()
/.../panic.go:12 +0x47
...
exit status 2
46. Defer
defer用于将某段代码推迟到当前函数的最后执行,一般用于清理操作。它类似于Java中的finally,但不同的是,遇到panic后不保证defer的语句一定能执行,除非使用后面讲到的recover恢复。
package main
import (
"fmt"
"os"
)
func main() {
f := createFile("/tmp/defer.txt")
// 将关闭文件的逻辑推迟到写完后
defer closeFile(f)
writeFile(f)
}
func createFile(p string) *os.File {
fmt.Println("creating")
f, err := os.Create(p)
if err != nil {
panic(err)
}
return f
}
func writeFile(f *os.File) {
fmt.Println("writing")
fmt.Fprintln(f, "data")
}
func closeFile(f *os.File) {
fmt.Println("closing")
err := f.Close()
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
}
$ go run defer.go
creating
writing
closing
47. Recover
Go提供了从panic中恢复的机制。类似于Java中的catch exception,可定义恢复后执行的操作。恢复通过recover函数实现,recover必须在defer函数中调用,在panic后会自动激活。可用这种方式确保defer函数一定得到执行。
package main
import "fmt"
func mayPanic() {
panic("a problem")
}
func main() {
defer func() {
// recover必须在defer函数中调用
if r := recover(); r != nil {
fmt.Println("Recovered. Error:\n", r)
}
}()
mayPanic()
fmt.Println("After mayPanic()")
}
$ go run recover.go
Recovered. Error:
a problem
48. String Functions
Go内置的strings包提供了很多有用的string处理函数,如Contains(是否包含)、Join(连接)、Split(切分),具体参见如下示例。
package main
import (
"fmt"
s "strings"
)
var p = fmt.Println
func main() {
p("Contains: ", s.Contains("test", "es"))
p("Count: ", s.Count("test", "t"))
p("HasPrefix: ", s.HasPrefix("test", "te"))
p("HasSuffix: ", s.HasSuffix("test", "st"))
p("Index: ", s.Index("test", "e"))
p("Join: ", s.Join([]string{"a", "b"}, "-"))
p("Repeat: ", s.Repeat("a", 5))
p("Replace: ", s.Replace("foo", "o", "0", -1))
p("Replace: ", s.Replace("foo", "o", "0", 1))
p("Split: ", s.Split("a-b-c-d-e", "-"))
p("ToLower: ", s.ToLower("TEST"))
p("ToUpper: ", s.ToUpper("test"))
}
$ go run string-functions.go
Contains: true
Count: 2
HasPrefix: true
HasSuffix: true
Index: 1
Join: a-b
Repeat: aaaaa
Replace: f00
Replace: f0o
Split: [a b c d e]
ToLower: test
ToUpper: TEST
49. String Formatting
Go通过Printf提供了方便的格式化功能,例如%v(结构的值)、%d(数字)、%s(字符串),具体参见如下示例。
package main
import (
"fmt"
"os"
)
type point struct {
x, y int
}
func main() {
p := point{1, 2}
fmt.Printf("struct1: %v\n", p)
fmt.Printf("struct2: %+v\n", p)
fmt.Printf("struct3: %#v\n", p)
fmt.Printf("type: %T\n", p)
fmt.Printf("bool: %t\n", true)
fmt.Printf("int: %d\n", 123)
fmt.Printf("bin: %b\n", 14)
fmt.Printf("char: %c\n", 33)
fmt.Printf("hex: %x\n", 456)
fmt.Printf("float1: %f\n", 78.9)
fmt.Printf("float2: %e\n", 123400000.0)
fmt.Printf("float3: %E\n", 123400000.0)
fmt.Printf("str1: %s\n", "\"string\"")
fmt.Printf("str2: %q\n", "\"string\"")
fmt.Printf("str3: %x\n", "hex this")
fmt.Printf("pointer: %p\n", &p)
fmt.Printf("width1: |%6d|%6d|\n", 12, 345)
fmt.Printf("width2: |%6.2f|%6.2f|\n", 1.2, 3.45)
fmt.Printf("width3: |%-6.2f|%-6.2f|\n", 1.2, 3.45)
fmt.Printf("width4: |%6s|%6s|\n", "foo", "b")
fmt.Printf("width5: |%-6s|%-6s|\n", "foo", "b")
s := fmt.Sprintf("sprintf: a %s", "string")
fmt.Println(s)
fmt.Fprintf(os.Stderr, "io: an %s\n", "error")
}
$ go run string-formatting.go
struct1: {1 2}
struct2: {x:1 y:2}
struct3: main.point{x:1, y:2}
type: main.point
bool: true
int: 123
bin: 1110
char: !
hex: 1c8
float1: 78.900000
float2: 1.234000e+08
float3: 1.234000E+08
str1: "string"
str2: "\"string\""
str3: 6865782074686973
pointer: 0xc0000ba000
width1: | 12| 345|
width2: | 1.20| 3.45|
width3: |1.20 |3.45 |
width4: | foo| b|
width5: |foo |b |
sprintf: a string
io: an error
50. Text Templates
Go提供了使用模板动态生成字符串或者HTML的功能。基于给定的模板,提供参数动态替换其中的占位符,可以有助于高效生成我们想要的文字。
package main
import (
"os"
"text/template"
)
func main() {
t1 := template.New("t1")
t1, err := t1.Parse("Value is {{.}}\n")
if err != nil {
panic(err)
}
// Must是上面包装err成panic的简便写法
t1 = template.Must(t1.Parse("Value: {{.}}\n"))
t1.Execute(os.Stdout, "some text")
t1.Execute(os.Stdout, 5)
t1.Execute(os.Stdout, []string{
"Go",
"Rust",
"C++",
"C#",
})
Create := func(name, t string) *template.Template {
return template.Must(template.New(name).Parse(t))
}
// 支持用字段名替换
t2 := Create("t2", "Name: {{.Name}}\n")
t2.Execute(os.Stdout, struct {
Name string
}{"Jane Doe"})
t2.Execute(os.Stdout, map[string]string{
"Name": "Mickey Mouse",
})
// 支持基于条件判断的替换
t3 := Create("t3",
"{{if . -}} yes {{else -}} no {{end}}\n")
t3.Execute(os.Stdout, "not empty")
t3.Execute(os.Stdout, "")
// 支持基于遍历的替换
t4 := Create("t4",
"Range: {{range .}}{{.}} {{end}}\n")
t4.Execute(os.Stdout,
[]string{
"Go",
"Rust",
"C++",
"C#",
})
}
$ go run templates.go
Value: some text
Value: 5
Value: [Go Rust C++ C#]
Name: Jane Doe
Name: Mickey Mouse
yes
no
Range: Go Rust C++ C#
51. Regular Expressions
像许多其他语言一样,Go也对正则表达式这一通用功能有良好支持。
package main
import (
"bytes"
"fmt"
"regexp"
)
func main() {
// 直接判断正则表达式和文字是否匹配
match, _ := regexp.MatchString("p([a-z]+)ch", "peach")
fmt.Println(match)
// 解析正则表达式。注意对于特殊符号,无需像Java一样用反斜杠转义
r, _ := regexp.Compile("p([a-z]+)ch")
// 再用解析好的正则,判断文字是否匹配
fmt.Println(r.MatchString("peach"))
fmt.Println(r.FindString("peach punch"))
fmt.Println("idx:", r.FindStringIndex("peach punch"))
fmt.Println(r.FindStringSubmatch("peach punch"))
fmt.Println(r.FindStringSubmatchIndex("peach punch"))
fmt.Println(r.FindAllString("peach punch pinch", -1))
fmt.Println("all:", r.FindAllStringSubmatchIndex(
"peach punch pinch", -1))
fmt.Println(r.FindAllString("peach punch pinch", 2))
fmt.Println(r.Match([]byte("peach")))
// 强制解析正则,失败则会panic
r = regexp.MustCompile("p([a-z]+)ch")
fmt.Println("regexp:", r)
fmt.Println(r.ReplaceAllString("a peach", "<fruit>"))
in := []byte("a peach")
out := r.ReplaceAllFunc(in, bytes.ToUpper)
fmt.Println(string(out))
}
$ go run regular-expressions.go
true
true
peach
idx: [0 5]
[peach ea]
[0 5 1 3]
[peach punch pinch]
all: [[0 5 1 3] [6 11 7 9] [12 17 13 15]]
[peach punch]
true
regexp: p([a-z]+)ch
a <fruit>
a PEACH
52. JSON
Go语言对JSON提供了内置的支持,包括Marshal(编码)、Unmarshal(解码)。
package main
import (
"encoding/json"
"fmt"
"os"
)
type response1 struct {
Page int
Fruits []string
}
type response2 struct {
Page int `json:"page"`
Fruits []string `json:"fruits"`
}
func main() {
bolB, _ := json.Marshal(true)
fmt.Println(string(bolB))
intB, _ := json.Marshal(1)
fmt.Println(string(intB))
fltB, _ := json.Marshal(2.34)
fmt.Println(string(fltB))
strB, _ := json.Marshal("gopher")
fmt.Println(string(strB))
slcD := []string{"apple", "peach", "pear"}
slcB, _ := json.Marshal(slcD)
fmt.Println(string(slcB))
mapD := map[string]int{"apple": 5, "lettuce": 7}
mapB, _ := json.Marshal(mapD)
fmt.Println(string(mapB))
res1D := &response1{
Page: 1,
Fruits: []string{"apple", "peach", "pear"}}
res1B, _ := json.Marshal(res1D)
fmt.Println(string(res1B))
res2D := &response2{
Page: 1,
Fruits: []string{"apple", "peach", "pear"}}
res2B, _ := json.Marshal(res2D)
fmt.Println(string(res2B))
byt := []byte(`{"num":6.13,"strs":["a","b"]}`)
var dat map[string]interface{}
if err := json.Unmarshal(byt, &dat); err != nil {
panic(err)
}
fmt.Println(dat)
num := dat["num"].(float64)
fmt.Println(num)
strs := dat["strs"].([]interface{})
str1 := strs[0].(string)
fmt.Println(str1)
str := `{"page": 1, "fruits": ["apple", "peach"]}`
res := response2{}
json.Unmarshal([]byte(str), &res)
fmt.Println(res)
fmt.Println(res.Fruits[0])
enc := json.NewEncoder(os.Stdout)
d := map[string]int{"apple": 5, "lettuce": 7}
enc.Encode(d)
}
$ go run json.go
true
1
2.34
"gopher"
["apple","peach","pear"]
{"apple":5,"lettuce":7}
{"Page":1,"Fruits":["apple","peach","pear"]}
{"page":1,"fruits":["apple","peach","pear"]}
map[num:6.13 strs:[a b]]
6.13
a
{1 [apple peach]}
apple
{"apple":5,"lettuce":7}
53. XML
Go语言对XML提供了内置的支持,包括Marshal(编码)、Unmarshal(解码)。
package main
import (
"encoding/xml"
"fmt"
)
type Plant struct {
XMLName xml.Name `xml:"plant"`
Id int `xml:"id,attr"`
Name string `xml:"name"`
Origin []string `xml:"origin"`
}
func (p Plant) String() string {
return fmt.Sprintf("Plant id=%v, name=%v, origin=%v",
p.Id, p.Name, p.Origin)
}
func main() {
coffee := &Plant{Id: 27, Name: "Coffee"}
coffee.Origin = []string{"Ethiopia", "Brazil"}
out, _ := xml.MarshalIndent(coffee, " ", " ")
fmt.Println(string(out))
fmt.Println(xml.Header + string(out))
var p Plant
if err := xml.Unmarshal(out, &p); err != nil {
panic(err)
}
fmt.Println(p)
tomato := &Plant{Id: 81, Name: "Tomato"}
tomato.Origin = []string{"Mexico", "California"}
type Nesting struct {
XMLName xml.Name `xml:"nesting"`
Plants []*Plant `xml:"parent>child>plant"`
}
nesting := &Nesting{}
nesting.Plants = []*Plant{coffee, tomato}
out, _ = xml.MarshalIndent(nesting, " ", " ")
fmt.Println(string(out))
}
$ go run xml.go
<plant id="27">
<name>Coffee</name>
<origin>Ethiopia</origin>
<origin>Brazil</origin>
</plant>
<?xml version="1.0" encoding="UTF-8"?>
<plant id="27">
<name>Coffee</name>
<origin>Ethiopia</origin>
<origin>Brazil</origin>
</plant>
Plant id=27, name=Coffee, origin=[Ethiopia Brazil]
<nesting>
<parent>
<child>
<plant id="27">
<name>Coffee</name>
<origin>Ethiopia</origin>
<origin>Brazil</origin>
</plant>
<plant id="81">
<name>Tomato</name>
<origin>Mexico</origin>
<origin>California</origin>
</plant>
</child>
</parent>
</nesting>
54. Time
下面例子展示了Go的time包对时间的操作。
package main
import (
"fmt"
"time"
)
func main() {
p := fmt.Println
// 获取当前时间
now := time.Now()
p(now)
// 创建一个指定的时间
then := time.Date(
2009, 11, 17, 20, 34, 58, 651387237, time.UTC)
p(then)
// 打印出时间的某项属性
p(then.Year())
p(then.Month())
p(then.Day())
p(then.Hour())
p(then.Minute())
p(then.Second())
p(then.Nanosecond())
p(then.Location())
p(then.Weekday())
p(then.Before(now))
p(then.After(now))
p(then.Equal(now))
// 注意:两个时间计算差值,返回的是duration
diff := now.Sub(then)
p(diff)
p(diff.Hours())
p(diff.Minutes())
p(diff.Seconds())
p(diff.Nanoseconds())
p(then.Add(diff))
p(then.Add(-diff))
}
$ go run time.go
2012-10-31 15:50:13.793654 +0000 UTC
2009-11-17 20:34:58.651387237 +0000 UTC
2009
November
17
20
34
58
651387237
UTC
Tuesday
true
false
false
25891h15m15.142266763s
25891.25420618521
1.5534752523711128e+06
9.320851514226677e+07
93208515142266763
2012-10-31 15:50:13.793654 +0000 UTC
2006-12-05 01:19:43.509120474 +0000 UTC
55. Epoch
Go也提供了基于时间得到时间戳,即从1970-1-1 0点到指定时间经历的秒、毫秒、纳秒数。
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
fmt.Println(now)
// 将时间转成基于秒、毫秒、纳秒的时间戳
fmt.Println(now.Unix())
fmt.Println(now.UnixMilli())
fmt.Println(now.UnixNano())
// 可以从时间戳反转成时间
fmt.Println(time.Unix(now.Unix(), 0))
fmt.Println(time.Unix(0, now.UnixNano()))
}
$ go run epoch.go
2012-10-31 16:13:58.292387 +0000 UTC
1351700038
1351700038292
1351700038292387000
2012-10-31 16:13:58 +0000 UTC
2012-10-31 16:13:58.292387 +0000 UTC
56. Time Formatting / Parsing
下面例子展示了日期格式化和解析的方法。
package main
import (
"fmt"
"time"
)
func main() {
p := fmt.Println
// 使用RFC3339标准格式化
t := time.Now()
p(t.Format(time.RFC3339))
// 使用RFC3339标准解析
t1, e := time.Parse(
time.RFC3339,
"2012-11-01T22:08:41+00:00")
p(t1)
// 格式化可以使用样例作为参数
p(t.Format("3:04PM"))
p(t.Format("Mon Jan _2 15:04:05 2006"))
p(t.Format("2006-01-02T15:04:05.999999-07:00"))
form := "3 04 PM"
t2, e := time.Parse(form, "8 41 PM")
p(t2)
fmt.Printf("%d-%02d-%02dT%02d:%02d:%02d-00:00\n",
t.Year(), t.Month(), t.Day(),
t.Hour(), t.Minute(), t.Second())
ansic := "Mon Jan _2 15:04:05 2006"
_, e = time.Parse(ansic, "8:41PM")
p(e)
}
$ go run time-formatting-parsing.go
2014-04-15T18:00:15-07:00
2012-11-01 22:08:41 +0000 +0000
6:00PM
Tue Apr 15 18:00:15 2014
2014-04-15T18:00:15.161182-07:00
0000-01-01 20:41:00 +0000 UTC
2014-04-15T18:00:15-00:00
parsing time "8:41PM" as "Mon Jan _2 15:04:05 2006": ...
57. Random Numbers
下面的例子展示了随机数的使用。
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
// 随机生成[0, 100)范围内的整数
fmt.Print(rand.Intn(100), ",")
fmt.Print(rand.Intn(100))
fmt.Println()
// 随机生成[0, 1)范围内的float64小数
fmt.Println(rand.Float64())
fmt.Print((rand.Float64()*5)+5, ",")
fmt.Print((rand.Float64() * 5) + 5)
fmt.Println()
// 随机函数默认以当前时间戳为种子,你也可以显式为它指定种子
s1 := rand.NewSource(time.Now().UnixNano())
r1 := rand.New(s1)
fmt.Print(r1.Intn(100), ",")
fmt.Print(r1.Intn(100))
fmt.Println()
// 用同一种子生成的随机数序列必定相同
s2 := rand.NewSource(42)
r2 := rand.New(s2)
fmt.Print(r2.Intn(100), ",")
fmt.Print(r2.Intn(100))
fmt.Println()
s3 := rand.NewSource(42)
r3 := rand.New(s3)
fmt.Print(r3.Intn(100), ",")
fmt.Print(r3.Intn(100))
}
$ go run random-numbers.go
81,87
0.6645600532184904
7.123187485356329,8.434115364335547
0,28
5,87
5,87
58. Number Parsing
下面例子展示了如何从string解析出数字。
package main
import (
"fmt"
"strconv"
)
func main() {
// 解析64位精度的float
f, _ := strconv.ParseFloat("1.234", 64)
fmt.Println(f)
// 解析64位整数,0代表从string自动推断数字的进制,此处为10进制
i, _ := strconv.ParseInt("123", 0, 64)
fmt.Println(i)
d, _ := strconv.ParseInt("0x1c8", 0, 64)
fmt.Println(d)
u, _ := strconv.ParseUint("789", 0, 64)
fmt.Println(u)
// Atoi是解析10进制整数的简便写法
k, _ := strconv.Atoi("135")
fmt.Println(k)
_, e := strconv.Atoi("wat")
fmt.Println(e)
}
$ go run number-parsing.go
1.234
123
456
789
135
strconv.ParseInt: parsing "wat": invalid syntax
59. URL Parsing
下面例子展示了从一个URL解析出用户名、密码、端口、文件路径等信息的方法。
package main
import (
"fmt"
"net"
"net/url"
)
func main() {
s := "postgres://user:pass@host.com:5432/path?k=v#f"
u, err := url.Parse(s)
if err != nil {
panic(err)
}
fmt.Println(u.Scheme)
fmt.Println(u.User)
fmt.Println(u.User.Username())
p, _ := u.User.Password()
fmt.Println(p)
fmt.Println(u.Host)
host, port, _ := net.SplitHostPort(u.Host)
fmt.Println(host)
fmt.Println(port)
fmt.Println(u.Path)
fmt.Println(u.Fragment)
fmt.Println(u.RawQuery)
m, _ := url.ParseQuery(u.RawQuery)
fmt.Println(m)
fmt.Println(m["k"][0])
}
$ go run url-parsing.go
postgres
user:pass
user
pass
host.com:5432
host.com
5432
/path
f
k=v
map[k:[v]]
v
60. SHA256 Hashes
SHA256哈希经常被用来对文件或文字做签名。例如TLS/SSL就使用它计算证书签名。
package main
import (
"crypto/sha256"
"fmt"
)
func main() {
s := "sha256 this string"
h := sha256.New()
h.Write([]byte(s))
// 计算SHA256哈希
bs := h.Sum(nil)
fmt.Println(s)
fmt.Printf("%x\n", bs)
}
$ go run sha256-hashes.go
sha256 this string
1af1dfa857bf1d8814fe1af8983c18080019922e557f15a8a...