V
Vel·ToolKit
简洁 · 高效 · 即开即用
ZH
第 17 章 / 共 20 章

标准库速览

fmt / strings / time / json / context / sort / slog 等

Go 标准库覆盖网络、IO、文本、加密、压缩、并发、模板等绝大多数日常需求。下面挑实战中最常用的几个包过一遍——掌握它们能让你 80% 的功能不依赖三方库。

fmt:格式化 I/O

Printf / Sprintf / Fprintf / Errorf 是同一套占位符系统。常用占位符:

%v   默认格式
%+v  结构体带字段名
%#v  Go 语法字面量
%T   类型
%d   十进制整数
%b %o %x %X  二/八/十六进制
%f %e %g     浮点数
%.2f         保留 2 位小数
%s   字符串
%q   带引号字符串(自动转义)
%c   rune 字符
%U   Unicode 码点(U+4E2D)
%t   布尔
%p   指针地址
%w   包装 error(仅 fmt.Errorf)
宽度对齐:%-8s 左对齐 8 字符;%8d 右对齐 8 字符
package main

import (
    "errors"
    "fmt"
)

func main() {
    name, age, score := "Alice", 30, 95.5
    fmt.Printf("%-8s %3d %.2f\n", name, age, score)

    s := fmt.Sprintf("hello %s", name)
    fmt.Println(s)

    orig := errors.New("disk full")
    err := fmt.Errorf("save %q: %w", "out.txt", orig) // 用 %w 包装
    fmt.Println(err)
    fmt.Println(errors.Is(err, orig)) // true
}

strings / bytes

strings 操作不可变字符串;bytes 提供等价 API 但操作 []byte。两者函数几乎一一对应。

package main

import (
    "fmt"
    "strings"
)

func main() {
    s := "  Hello Go  "
    fmt.Println(strings.Contains(s, "Go"))                  // true
    fmt.Println(strings.HasPrefix("v1.22.0", "v1."))        // true
    fmt.Println(strings.Split("a,b,c", ","))                // [a b c]
    fmt.Println(strings.Join([]string{"a", "b"}, "/"))      // a/b
    fmt.Println(strings.ReplaceAll("a\r\nb", "\r\n", "\n")) // a\nb
    fmt.Println(strings.TrimSpace(s))                       // Hello Go
    fmt.Println(strings.Fields("  hello   go  "))           // [hello go]
    fmt.Println(strings.EqualFold("Go", "go"))              // true

    var b strings.Builder
    for i := 0; i < 100; i++ {
        b.WriteString("x")
    }
    fmt.Println(len(b.String())) // 100
}

strconv:字符串 ⇄ 数字

package main

import (
    "fmt"
    "strconv"
)

func main() {
    n, err := strconv.Atoi("42") // string -> int
    fmt.Println(n, err)

    s := strconv.Itoa(42) // int -> string
    fmt.Println(s)

    f, _ := strconv.ParseFloat("3.14", 64)        // string -> float64
    s2 := strconv.FormatFloat(3.14, 'f', 2, 64)    // "3.14"
    fmt.Println(f, s2)

    b, _ := strconv.ParseBool("true")
    fmt.Println(b)

    q := strconv.Quote(`he said "hi"`) // "he said \"hi\""
    fmt.Println(q)
}

time:时间与时长

time.Time 表示一个绝对时刻,time.Duration 是 int64 纳秒。Go 的格式化模板用一个固定的“魔法时间” 2006-01-02 15:04:05 ——01/02 03:04:05 PM '06 -0700 也对应着 1/2/3/4/5/6/7。

package main

import (
    "fmt"
    "time"
)

func main() {
    now := time.Now()
    fmt.Println(now.Format("2006-01-02 15:04:05"))
    fmt.Println(now.Format(time.RFC3339))

    t, err := time.Parse(time.RFC3339, "2026-05-12T08:00:00Z")
    fmt.Println(t, err)

    loc, _ := time.LoadLocation("Asia/Shanghai")
    fmt.Println(t.In(loc))

    d := 2*time.Hour + 30*time.Minute
    future := now.Add(d)
    fmt.Println(future.Sub(now)) // 2h30m0s

    fmt.Println(now.Unix(), now.UnixMilli(), now.UnixNano())
}

定时器与节流

package main

import (
    "fmt"
    "time"
)

func main() {
    // 延时
    time.Sleep(100 * time.Millisecond)

    // 一次性触发
    <-time.After(100 * time.Millisecond)

    // 周期性触发
    ticker := time.NewTicker(200 * time.Millisecond)
    defer ticker.Stop()
    stop := time.After(700 * time.Millisecond)
    for {
        select {
        case t := <-ticker.C:
            fmt.Println("tick", t.Format("15:04:05.000"))
        case <-stop:
            return
        }
    }
}

encoding/json

Marshal / Unmarshal 处理内存中的整块 JSON;NewEncoder / NewDecoder 适合流式(HTTP body、大文件)。结构体 tag 控制字段名、可选字段、忽略。

package main

import (
    "encoding/json"
    "fmt"
    "strings"
    "time"
)

type User struct {
    ID       int       `json:"id"`
    Name     string    `json:"name"`
    Email    string    `json:"email,omitempty"` // 空值省略
    Password string    `json:"-"`               // 永不输出
    Created  time.Time `json:"created_at"`
}

func main() {
    b, _ := json.MarshalIndent(User{1, "Alice", "", "pw", time.Now()}, "", "  ")
    fmt.Println(string(b))

    var u User
    _ = json.Unmarshal(b, &u)
    fmt.Println(u.Name)

    // 流式:处理超大 JSON 数组
    src := `[{"id":1,"name":"a","created_at":"2026-05-12T00:00:00Z"},{"id":2,"name":"b","created_at":"2026-05-12T00:00:00Z"}]`
    dec := json.NewDecoder(strings.NewReader(src))
    dec.Token() // 跳过开头的 [
    for dec.More() {
        var u User
        if err := dec.Decode(&u); err != nil {
            break
        }
        fmt.Println(u.ID, u.Name)
    }
}

context:取消、超时、传值

context 在调用链中传递取消信号、超时与请求级数据,是所有库公认的“函数第一个参数”。规则:父 context 取消时,所有派生子 context 一并取消。

package main

import (
    "context"
    "errors"
    "fmt"
    "log"
    "net/http"
    "time"
)

type traceKey struct{}

func main() {
    ctx := context.Background()                            // 根
    ctx, cancel := context.WithTimeout(ctx, 2*time.Second) // 超时
    defer cancel()                                         // 必须 defer cancel

    req, _ := http.NewRequestWithContext(ctx, "GET", "https://example.com", nil)
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        if errors.Is(err, context.DeadlineExceeded) {
            log.Println("timeout")
        } else {
            log.Println("err:", err)
        }
    } else {
        resp.Body.Close()
    }

    // 传请求级数据(只放贯穿调用链的元数据如 trace id)
    ctx = context.WithValue(ctx, traceKey{}, "req-123")
    trace := ctx.Value(traceKey{}).(string)
    fmt.Println("trace:", trace)
}

errors:包装、判断、拆包

package main

import (
    "errors"
    "fmt"
)

var ErrNotFound = errors.New("not found")

type User struct{ ID int }

func Get(id int) (*User, error) {
    if id == 0 {
        return nil, fmt.Errorf("get(%d): %w", id, ErrNotFound)
    }
    return &User{ID: id}, nil
}

func main() {
    _, err := Get(0)
    if errors.Is(err, ErrNotFound) {
        fmt.Println("not found")
    }

    // 多错误聚合(Go 1.20+)
    joined := errors.Join(errors.New("a"), errors.New("b"))
    fmt.Println(joined)
}

sort / slices / maps

Go 1.21+ 加入泛型化的 slices 和 maps 包,比旧 sort 包好用很多。

package main

import (
    "fmt"
    "maps"
    "slices"
    "sort"
    "strings"
)

type User struct {
    Name string
    Age  int
}

func main() {
    nums := []int{3, 1, 4, 1, 5, 9}
    slices.Sort(nums)
    fmt.Println(nums)

    users := []User{{"Bob", 30}, {"Alice", 25}}
    slices.SortFunc(users, func(a, b User) int {
        return strings.Compare(a.Name, b.Name)
    })
    fmt.Println(users)

    idx, ok := slices.BinarySearch(nums, 4)
    fmt.Println(idx, ok)
    fmt.Println(slices.Contains(nums, 5), slices.Max(nums))
    fmt.Println(slices.Compact(slices.Clone(nums))) // 去除相邻重复

    m := map[string]int{"a": 1, "b": 2}
    keys := slices.Sorted(maps.Keys(m))
    fmt.Println(keys)
    fmt.Println(maps.Clone(m))

    // 旧式 sort 仍可用
    sort.Slice(users, func(i, j int) bool { return users[i].Age < users[j].Age })
}

regexp

package main

import (
    "fmt"
    "regexp"
)

func main() {
    re := regexp.MustCompile(`(\w+)@(\w+\.\w+)`)
    fmt.Println(re.MatchString("a@b.c"))                 // true
    fmt.Println(re.FindStringSubmatch("a@b.c"))           // [a@b.c a b.c]
    fmt.Println(re.ReplaceAllString("a@b.c", "$1 at $2")) // a at b.c

    // 编译失败处理
    if _, err := regexp.Compile(`(`); err != nil {
        fmt.Println("bad pattern:", err)
    }
}

log/slog:结构化日志(Go 1.21+)

新版结构化日志库。支持 JSON / Text handler、属性分组、级别控制,是替换 logrus / zap 的官方选项。

package main

import (
    "errors"
    "log/slog"
    "os"
)

func main() {
    logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
        Level: slog.LevelInfo,
    }))
    slog.SetDefault(logger)

    slog.Info("user login",
        "user_id", 42,
        "ip", "127.0.0.1",
    )
    slog.Error("save failed", "err", errors.New("disk full"))
}

其它高频包

  • encoding/base64 · encoding/hex · encoding/csv
  • crypto/sha256 · crypto/hmac · crypto/rand
  • compress/gzip · archive/zip · archive/tar
  • net/url:URL 解析与查询字符串
  • os/exec:执行外部命令
  • os/signal:监听 SIGINT/SIGTERM 实现优雅停机
  • runtime/pprof · net/http/pprof:性能剖析
  • embed:把静态资源编进二进制(//go:embed)