• 跟着实例学Go语言(三)


    本教程全面涵盖了Go语言基础的各个方面。一共80个例子,每个例子对应一个语言特性点,既适合新人快速上手,也适合工作中遇到问题速查知识点。
    教程代码示例来自go by example,文字部分来自本人自己的理解。

    本文是教程系列的第三部分,共计20个例子、约1.5万字。

    系列文章快速跳转:
    跟着实例学Go语言(一)
    跟着实例学Go语言(二)
    跟着实例学Go语言(三)
    跟着实例学Go语言(四)

    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)
    }
    
    • 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
    $ go run mutexes.go
    map[a:20000 b:10000]
    
    • 1
    • 2

    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)
    }
    
    • 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
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    $ go run stateful-goroutines.go
    readOps: 71708
    writeOps: 7177
    
    • 1
    • 2
    • 3

    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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    $ go run sorting.go
    Strings: [a b c]
    Ints:    [2 4 7]
    Sorted:  true
    
    • 1
    • 2
    • 3
    • 4

    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)
    }
    
    • 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
    $ go run sorting-by-functions.go 
    [kiwi peach banana]
    
    • 1
    • 2

    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)
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    $ go run panic.go
    panic: a problem
    
    	
    // 程序会panic的那行直接退出,退出码不再是0
    goroutine 1 [running]:
    main.main()
        /.../panic.go:12 +0x47
    ...
    exit status 2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    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)
        }
    }
    
    • 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
    $ go run defer.go
    creating
    writing
    closing
    
    • 1
    • 2
    • 3
    • 4

    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()")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    $ go run recover.go
    Recovered. Error:
     a problem
    
    • 1
    • 2
    • 3

    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"))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    $ 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    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")
    }
    
    • 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
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    $ 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    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#",
            })
    }
    
    • 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
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    $ 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# 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    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", ""))
    
        in := []byte("a peach")
        out := r.ReplaceAllFunc(in, bytes.ToUpper)
        fmt.Println(string(out))
    }
    
    • 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
    $ 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    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)
    }
    
    • 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
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    $ 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}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    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))
    }
    
    • 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
    • 45
    • 46
    • 47
    • 48
    $ 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>
    
    • 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

    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))
    }
    
    • 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
    • 45
    $ 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    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()))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    $ 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    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)
    }
    
    • 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
    $ 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": ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    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))
    }
    
    • 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
    $ go run random-numbers.go
    81,87
    0.6645600532184904
    7.123187485356329,8.434115364335547
    0,28
    5,87
    5,87
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    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)
    }
    
    • 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
    $ go run number-parsing.go 
    1.234
    123
    456
    789
    135
    strconv.ParseInt: parsing "wat": invalid syntax
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    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])
    }
    
    • 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
    $ go run url-parsing.go 
    postgres
    user:pass
    user
    pass
    host.com:5432
    host.com
    5432
    /path
    f
    k=v
    map[k:[v]]
    v
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    $ go run sha256-hashes.go
    sha256 this string
    1af1dfa857bf1d8814fe1af8983c18080019922e557f15a8a...
    
    • 1
    • 2
    • 3
  • 相关阅读:
    高教社杯数模竞赛特辑论文篇-2023年C题:基于历史数据的蔬菜类商品定价与补货决策模型(附获奖论文及R语言和Python代码实现)
    自动化专业之半导体行业入门指南
    为什么混合云是未来云计算的主流形态?
    hbase和aerospike基础概念
    虚函数(多态)
    antd table表格支持多选框选择当前列,进行格式设置等
    截图取证,怎样保证质量
    第二部分:DDD中的值对象
    AVL树的插入详解
    离线数据仓库第二讲
  • 原文地址:https://blog.csdn.net/needmorecode/article/details/128180316