Go 标准库
Go 的标准库功能强大且全面,涵盖了网络、系统、文本处理等常见需求。“batteries included” 是 Go 的设计哲学之一——大多数场景无需引入第三方库。本篇介绍最常用的标准库包及其典型用法。
net/http:HTTP 服务器
net/http 包可以轻松创建 HTTP 服务器。
package main
import (
"encoding/json"
"fmt"
"net/http"
)
// 处理首页请求
func homeHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "欢迎使用 Go HTTP 服务器!\n")
fmt.Fprintf(w, "请求方法: %s, 路径: %s\n", r.Method, r.URL.Path)
}
// 处理 JSON API
func apiHandler(w http.ResponseWriter, r *http.Request) {
data := map[string]interface{}{
"status": "ok",
"message": "Hello, API!",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(data)
}
// 处理用户信息(带路径参数)
func userHandler(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
if name == "" {
name = "匿名"
}
fmt.Fprintf(w, "你好, %s!\n", name)
}
func main() {
http.HandleFunc("/", homeHandler)
http.HandleFunc("/api", apiHandler)
http.HandleFunc("/user", userHandler)
fmt.Println("服务器启动在 :8080")
http.ListenAndServe(":8080", nil)
}http.HandleFunc 注册路由处理函数,http.ListenAndServe 启动监听。http.ResponseWriter 用于写响应,*http.Request 包含请求信息。
net/http:HTTP 客户端
net/http 同样提供了 HTTP 客户端功能。
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"time"
)
func main() {
// === GET 请求 ===
resp, err := http.Get("https://httpbin.org/get")
if err != nil {
fmt.Println("请求失败:", err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Printf("GET 状态码: %d\n", resp.StatusCode)
fmt.Printf("GET 响应: %s\n\n", string(body[:200]))
// === POST 请求 ===
jsonData := `{"name": "张三", "age": 28}`
resp2, err := http.Post("https://httpbin.org/post", "application/json",
strings.NewReader(jsonData))
if err != nil {
fmt.Println("POST 失败:", err)
return
}
defer resp2.Body.Close()
body2, _ := io.ReadAll(resp2.Body)
fmt.Printf("POST 状态码: %d\n", resp2.StatusCode)
fmt.Printf("POST 响应: %s\n\n", string(body2[:200]))
// === 使用自定义 Client(带超时) ===
client := &http.Client{
Timeout: 5 * time.Second,
}
req, _ := http.NewRequest("GET", "https://httpbin.org/delay/1", nil)
req.Header.Set("Accept", "application/json")
resp3, err := client.Do(req)
if err != nil {
fmt.Println("请求失败:", err)
return
}
defer resp3.Body.Close()
var result map[string]interface{}
json.NewDecoder(resp3.Body).Decode(&result)
fmt.Printf("自定义 Client 响应: %v\n", result["url"])
}推荐使用自定义的 http.Client 而非全局的 http.Get/http.Post,因为可以设置超时和其他配置。
context 包
context 包用于在 goroutine 之间传递截止时间、取消信号和请求范围的值。
package main
import (
"context"
"fmt"
"time"
)
// 模拟一个耗时操作
func slowOperation(ctx context.Context) (string, error) {
select {
case <-time.After(2 * time.Second):
return "操作完成", nil
case <-ctx.Done():
return "", ctx.Err()
}
}
func main() {
// === 超时控制 ===
fmt.Println("--- 超时控制 ---")
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
result, err := slowOperation(ctx)
if err != nil {
fmt.Println("错误:", err) // 超时错误
} else {
fmt.Println("结果:", result)
}
// === 值传递 ===
fmt.Println("\n--- 值传递 ---")
ctx2 := context.WithValue(context.Background(), "userID", 42)
ctx3 := context.WithValue(ctx2, "role", "admin")
fmt.Println("userID:", ctx3.Value("userID"))
fmt.Println("role:", ctx3.Value("role"))
// === 取消传播 ===
fmt.Println("\n--- 取消传播 ---")
parent, parentCancel := context.WithCancel(context.Background())
child1, child1Cancel := context.WithCancel(parent)
defer child1Cancel()
child2, child2Cancel := context.WithCancel(parent)
defer child2Cancel()
go func() {
<-child1.Done()
fmt.Println("child1 被取消")
}()
go func() {
<-child2.Done()
fmt.Println("child2 被取消")
}()
parentCancel() // 取消 parent,child1 和 child2 也会被级联取消
time.Sleep(100 * time.Millisecond)
}context.Background() 创建根 context。WithTimeout 设置超时,WithCancel 手动取消,WithValue 传递键值对。context 是不可变的,派生操作会返回新的 context。
time 包
time 包提供时间和日期的处理功能。
package main
import (
"fmt"
"time"
)
func main() {
// === Time 基本操作 ===
now := time.Now()
fmt.Printf("当前时间: %s\n", now.Format("2006-01-02 15:04:05"))
// 解析时间
t, _ := time.Parse("2006-01-02", "2024-06-15")
fmt.Printf("解析时间: %s\n", t.Format("2006年01月02日"))
// 时间计算
tomorrow := now.AddDate(0, 0, 1)
fmt.Printf("明天: %s\n", tomorrow.Format("2006-01-02"))
// 时间差
diff := tomorrow.Sub(now)
fmt.Printf("时间差: %v\n", diff)
// === Duration ===
fmt.Println("\n--- Duration ---")
d := 2*time.Hour + 30*time.Minute
fmt.Printf("Duration: %v = %v 秒\n", d, d.Seconds())
// === Ticker(定时器) ===
fmt.Println("\n--- Ticker ---")
ticker := time.NewTicker(100 * time.Millisecond)
done := make(chan bool)
go func() {
count := 0
for {
select {
case <-ticker.C:
count++
fmt.Printf("Tick %d\n", count)
if count >= 3 {
ticker.Stop()
done <- true
return
}
}
}
}()
<-done
fmt.Println("Ticker 停止")
// === Timer(一次性定时器) ===
fmt.Println("\n--- Timer ---")
timer := time.NewTimer(200 * time.Millisecond)
<-timer.C
fmt.Println("Timer 到期")
}Go 的时间格式化使用固定的参考时间 2006-01-02 15:04:05(对应 1/2 3:04:05 ‘06 -0700)。Duration 表示时间间隔,Ticker 周期性触发,Timer 一次性触发。
log 包
log 包提供基本的日志功能。
package main
import (
"log"
"os"
)
func main() {
// 基本日志输出
log.Println("这是一条普通日志")
log.Printf("格式化日志: 用户 %s 已登录", "张三")
// 自定义 logger
file, err := os.OpenFile("/tmp/go-demo/app.log",
os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
log.Fatal("打开日志文件失败:", err)
}
defer file.Close()
// 同时输出到文件和控制台
// 这里简化为只输出到文件
logger := log.New(file, "[APP] ", log.Ldate|log.Ltime|log.Lshortfile)
logger.Println("这条日志写入文件")
logger.Printf("用户操作: %s", "查询数据")
// 日志级别模拟
infoLog := log.New(os.Stdout, "[INFO] ", log.LstdFlags)
warnLog := log.New(os.Stdout, "[WARN] ", log.LstdFlags)
errorLog := log.New(os.Stderr, "[ERROR] ", log.LstdFlags|log.Lshortfile)
infoLog.Println("服务启动成功")
warnLog.Println("内存使用率较高")
errorLog.Println("数据库连接失败")
}log.Fatal 输出日志后调用 os.Exit(1) 退出程序。log.Panic 输出日志后调用 panic。生产环境建议使用 slog(Go 1.21+)或第三方日志库如 zap。
fmt 包
fmt 包提供格式化 I/O 功能。
package main
import "fmt"
func main() {
// === Print 系列 ===
fmt.Print("无换行 ")
fmt.Println("有换行")
// === Sprintf(返回字符串) ===
name := "张三"
age := 28
s := fmt.Sprintf("姓名: %s, 年龄: %d", name, age)
fmt.Println(s)
// === Errorf(返回 error) ===
err := fmt.Errorf("用户 %s 年龄 %d 不合法", name, -1)
fmt.Println("错误:", err)
// === 常用格式化动词 ===
fmt.Println("\n--- 格式化示例 ---")
fmt.Printf("整数: %d\n", 42)
fmt.Printf("浮点: %.2f\n", 3.14159)
fmt.Printf("字符串: %s\n", "hello")
fmt.Printf("布尔: %t\n", true)
fmt.Printf("十六进制: %x\n", 255)
fmt.Printf("指针: %p\n", &name)
fmt.Printf("通用: %v\n", []int{1, 2, 3})
fmt.Printf("结构体: %+v\n", struct{ X int }{X: 10})
fmt.Printf("类型: %T\n", 42)
fmt.Printf("宽度: |%10s|\n", "hi")
fmt.Printf("左对齐: |%-10s|\n", "hi")
fmt.Printf("补零: |%05d|\n", 42)
}strings 和 bytes 包
strings 处理字符串,bytes 处理字节切片,API 基本对称。
package main
import (
"bytes"
"fmt"
"strings"
)
func main() {
s := " Hello, Go World! "
// 大小写
fmt.Println("大写:", strings.ToUpper("hello"))
fmt.Println("小写:", strings.ToLower("HELLO"))
// 查找与判断
fmt.Println("包含:", strings.Contains(s, "Go"))
fmt.Println("前缀:", strings.HasPrefix(strings.TrimSpace(s), "Hello"))
fmt.Println("后缀:", strings.HasSuffix(strings.TrimSpace(s), "!"))
// 分割与连接
parts := strings.Split("a,b,c,d", ",")
fmt.Println("分割:", parts)
fmt.Println("连接:", strings.Join(parts, " | "))
// 替换与修剪
fmt.Println("替换:", strings.Replace("hello world", "world", "Go", 1))
fmt.Println("修剪:", strings.TrimSpace(s))
fmt.Println("修剪字符:", strings.Trim("###title###", "#"))
// Builder(高效拼接)
var builder strings.Builder
for i := 0; i < 5; i++ {
fmt.Fprintf(&builder, "item-%d ", i)
}
fmt.Println("Builder:", builder.String())
// bytes.Buffer(字节缓冲)
var buf bytes.Buffer
buf.WriteString("Hello ")
buf.WriteString("World")
fmt.Println("Buffer:", buf.String())
fmt.Println("Buffer 字节:", buf.Bytes())
}os 包
os 包提供操作系统相关的功能。
package main
import (
"fmt"
"os"
)
func main() {
// 环境变量
os.Setenv("APP_ENV", "production")
fmt.Println("APP_ENV:", os.Getenv("APP_ENV"))
fmt.Println("HOME:", os.Getenv("HOME"))
// 命令行参数
fmt.Println("\n程序名:", os.Args[0])
fmt.Printf("参数数量: %d\n", len(os.Args))
// 进程信息
fmt.Println("\n进程ID:", os.Getpid())
fmt.Println("父进程ID:", os.Getppid())
fmt.Println("工作目录:", func() string {
dir, _ := os.Getwd()
return dir
}())
// 临时目录
fmt.Println("临时目录:", os.TempDir())
// 退出(注释掉避免程序退出)
// os.Exit(0)
}总结
Go 标准库覆盖了日常开发的大部分需求。net/http 提供 HTTP 服务器和客户端;context 管理 goroutine 的超时和取消;time 处理时间和日期;log 提供日志功能;fmt 负责格式化 I/O;strings/bytes 处理文本和字节数据;os 与操作系统交互。Go 标准库的设计简洁一致,掌握这些核心包可以应对绝大多数开发场景,减少对外部依赖的需要。