编程 Docker 出品的 go-units:一行代码把 1.5GB 转成字节,别再手写正则解析单位

2026-09-12 09:37:25

用 go-units 解析 1.5GB、格式化 9000 秒和 Docker 资源限制

项目地址:github.com/docker/go-units

1. 为什么需要 go-units

用户输入 "1KB""1MB""1GB",需要转换为字节数时,常见做法是手写字符串处理。把秒数转成 "2 hours 30 minutes" 这样的可读格式,也要自己处理转换逻辑。做 Docker 相关项目时,还会碰到容器资源限制里的各种单位。

go-units 是 Docker 团队维护的单位转换库,用来处理大小、时间、字节转换。

先看一个真实场景:用户输入文件大小 "1.5GB",需要转换为字节数。用正则表达式:

func parseSize(size string) (int64, error) {
    re := regexp.MustCompile(`^(\d+(?:\.\d+)?)\s*([KMGTPE]?)B?$`)
    matches := re.FindStringSubmatch(size)
    if len(matches) != 3 {
        return 0, fmt.Errorf("invalid size format")
    }

    value, _ := strconv.ParseFloat(matches[1], 64)
    unit := matches[2]

    var multiplier int64 = 1
    switch unit {
    case "K":
        multiplier = 1024
    case "M":
        multiplier = 1024 * 1024
    case "G":
        multiplier = 1024 * 1024 * 1024
    case "T":
        multiplier = 1024 * 1024 * 1024 * 1024
    }

    return int64(value * float64(multiplier)), nil
}

bytes, err := parseSize("1.5GB")

用 go-units:

import "github.com/docker/go-units"

bytes, err := units.RAMInBytes("1.5GB")
// bytes = 1610612736

一行代码。

2. go-units 是什么

go-units 是一个 Go 语言的单位转换库。官方描述是:

Unit conversion library for Docker.

它面向 Docker,支持大小、时间、字节等转换。

核心定位

go-units 不是数据处理库,也不是数据可视化库。它是一个单位转换库,专门用于处理各种单位的转换。

它做的事情:

  1. 字符串 → 字节数:"1.5GB"1610612736
  2. 字节数 → 字符串:1610612736"1.5 GiB"
  3. 秒数 → 可读时间:9000"2 hours 30 minutes"
  4. 时间 → 秒数:"2 hours"7200

功能不复杂,覆盖的是单位转换的常见需求。

3. 核心特性:大小、时间、字节

大小转换

import "github.com/docker/go-units"

// 字符串 → 字节
bytes, _ := units.RAMInBytes("1.5GB")
fmt.Println(bytes) // 1610612736

// 字节 → 字符串
size := units.BytesSize(1610612736)
fmt.Println(size) // 1.5 GiB

// SI 格式(1000 进制)
size = units.BytesSizeBase10(1500000000)
fmt.Println(size) // 1.5 GB

时间转换

// 秒数 → 可读时间
duration := units.HumanDuration(9000 * time.Second)
fmt.Println(duration) // 2 hours 30 minutes

// 字符串 → 时间
d, _ := time.ParseDuration("2h30m")
fmt.Println(d.Seconds()) // 9000

字节格式化

// 精确格式化
size := units.BytesSize(1234567)
fmt.Println(size) // 1.18 MiB

// 整数格式化
size = units.BytesSize(1024)
fmt.Println(size) // 1 KiB

4. 安装和使用

安装

go get github.com/docker/go-units

基本使用

1. 解析大小

package main

import (
    "fmt"

    "github.com/docker/go-units"
)

func main() {
    // 解析各种格式
    sizes := []string{
        "1B",
        "1KB",
        "1MB",
        "1GB",
        "1.5GB",
        "1GiB",
    }

    for _, s := range sizes {
        bytes, err := units.RAMInBytes(s)
        if err != nil {
            fmt.Printf("Error parsing %s: %v\n", s, err)
            continue
        }
        fmt.Printf("%s = %d bytes\n", s, bytes)
    }
}

2. 格式化大小

func main() {
    sizes := []int64{
        1024,
        1048576,
        1073741824,
        1610612736,
    }

    for _, s := range sizes {
        fmt.Printf("%d bytes = %s\n", s, units.BytesSize(float64(s)))
    }
}

3. 时间格式化

func main() {
    durations := []time.Duration{
        30 * time.Second,
        5 * time.Minute,
        2 * time.Hour,
        2*time.Hour + 30*time.Minute,
        25 * time.Hour,
    }

    for _, d := range durations {
        fmt.Printf("%v = %s\n", d, units.HumanDuration(d))
    }
}

4. 解析时间

func main() {
    times := []string{
        "30s",
        "5m",
        "2h",
        "2h30m",
        "25h",
    }

    for _, t := range times {
        d, err := time.ParseDuration(t)
        if err != nil {
            fmt.Printf("Error parsing %s: %v\n", t, err)
            continue
        }
        fmt.Printf("%s = %v (%s)\n", t, d, units.HumanDuration(d))
    }
}

5. 核心功能

功能一:精确大小解析

func main() {
    // 精确解析,支持各种格式
    inputs := []string{
        "1024",
        "1KB",
        "1 KiB",
        "1.5 GB",
        "1.5GiB",
    }

    for _, input := range inputs {
        bytes, err := units.FromHumanSize(input)
        if err != nil {
            fmt.Printf("Error: %v\n", err)
            continue
        }
        fmt.Printf("%s = %d bytes\n", input, bytes)
    }
}

功能二:文件大小格式化

func main() {
    sizes := []float64{
        500,
        1024,
        1048576,
        1073741824,
        1099511627776,
    }

    for _, size := range sizes {
        // IEC 格式(1024 进制)
        fmt.Printf("%.0f bytes = %s\n", size, units.BytesSize(size))

        // SI 格式(1000 进制)
        fmt.Printf("%.0f bytes = %s\n", size, units.BytesSizeBase10(size))
    }
}

功能三:时间格式化

func main() {
    // 各种时间长度
    durations := []time.Duration{
        time.Second,
        time.Minute,
        time.Hour,
        24 * time.Hour,
        7 * 24 * time.Hour,
    }

    for _, d := range durations {
        fmt.Printf("%v = %s\n", d, units.HumanDuration(d))
    }
}

功能四:自定义格式化

func formatBytes(bytes int64) string {
    const unit = 1024
    if bytes < unit {
        return fmt.Sprintf("%d B", bytes)
    }

    div, exp := int64(unit), 0
    for n := bytes / unit; n >= unit; n /= unit {
        div *= unit
        exp++
    }

    return fmt.Sprintf("%.1f %ciB", float64(bytes)/float64(div), "KMGTPE"[exp])
}

func main() {
    fmt.Println(formatBytes(1234567)) // 1.2 MiB
}

6. 实战场景

场景一:Docker 资源限制

问题:解析 Docker 容器的内存限制。

type ContainerConfig struct {
    Name      string
    Memory    string
    CPUPeriod string
}

func ParseContainerConfig(config ContainerConfig) (int64, error) {
    // 解析内存限制
    memory, err := units.RAMInBytes(config.Memory)
    if err != nil {
        return 0, fmt.Errorf("invalid memory: %w", err)
    }

    fmt.Printf("Container %s: Memory = %d bytes (%s)\n",
        config.Name, memory, units.BytesSize(float64(memory)))

    return memory, nil
}

func main() {
    configs := []ContainerConfig{
        {"web-server", "512MB", "100000"},
        {"database", "2GB", "200000"},
        {"cache", "1GiB", "50000"},
    }

    for _, config := range configs {
        memory, err := ParseContainerConfig(config)
        if err != nil {
            fmt.Printf("Error: %v\n", err)
            continue
        }
        fmt.Printf("Parsed: %d bytes\n\n", memory)
    }
}

效果:自动解析 "512MB""2GB""1GiB" 等格式。

场景二:磁盘空间显示

问题:显示磁盘使用情况,需要人类可读。

type DiskUsage struct {
    Total int64
    Used  int64
    Free  int64
}

func DisplayDiskUsage(usage DiskUsage) {
    usedPercent := float64(usage.Used) / float64(usage.Total) * 100

    fmt.Println("=== 磁盘使用情况 ===")
    fmt.Printf("总空间:%s\n", units.BytesSize(float64(usage.Total)))
    fmt.Printf("已使用:%s (%.1f%%)\n",
        units.BytesSize(float64(usage.Used)), usedPercent)
    fmt.Printf("可用:%s\n", units.BytesSize(float64(usage.Free)))
}

func main() {
    usage := DiskUsage{
        Total: 500 * 1024 * 1024 * 1024, // 500 GB
        Used:  250 * 1024 * 1024 * 1024, // 250 GB
        Free:  250 * 1024 * 1024 * 1024, // 250 GB
    }

    DisplayDiskUsage(usage)
}

效果:显示 "465.7 GiB""232.8 GiB (50.0%)" 等可读格式。

场景三:运行时间显示

问题:显示程序运行时间,需要人类可读。

type ProcessInfo struct {
    Name      string
    StartTime time.Time
}

func DisplayProcessInfo(info ProcessInfo) {
    runtime := time.Since(info.StartTime)

    fmt.Printf("进程:%s\n", info.Name)
    fmt.Printf("启动时间:%s\n", info.StartTime.Format("2006-01-02 15:04:05"))
    fmt.Printf("运行时间:%s\n", units.HumanDuration(runtime))
}

func main() {
    info := ProcessInfo{
        Name:      "web-server",
        StartTime: time.Now().Add(-2*time.Hour - 30*time.Minute),
    }

    DisplayProcessInfo(info)
}

效果:显示 "2 hours 30 minutes" 等可读格式。

7. 设计亮点

亮点一:Docker 出品

Docker 团队开发,经过大规模验证,稳定可靠,持续维护。

亮点二:格式全面

支持各种格式的大小、时间。一个库覆盖需求,减少依赖,统一风格。

亮点三:双向转换

支持格式化和解析。既能显示给用户,也能从用户输入解析,数据往返一致。

亮点四:IEC/SI 双标准

支持 1024 进制和 1000 进制。适应不同场景,符合行业标准,选择灵活。

亮点五:零依赖

纯 Go 实现,无外部依赖。易于集成,编译快,跨平台。

8. 和类似方案对比

vs. go-humanize

  • go-units:约 5K Star,Docker 团队维护,专注单位转换,大小解析强。
  • go-humanize:约 2.8K Star,个人项目,偏数据格式化,大小解析一般。

结论:go-units 更专业,go-humanize 更全面。

vs. 手动转换

  • go-units:代码量少,可维护性高,正确性高,格式支持丰富。
  • 手动转换:代码量多,可维护性低,容易出错,格式支持有限。

结论:go-units 更高效,手动转换更灵活。

vs. 其他单位库

  • go-units:约 5K Star,原生支持 Docker,维护活跃。
  • golang-units:约 1K Star,无 Docker 支持,维护较少。

结论:go-units 是 Docker 生态标准。

9. 局限性

go-units 也有局限:

  1. 功能单一:只做单位转换
  2. Docker 导向:主要服务于 Docker
  3. 格式有限:某些特殊格式不支持
  4. 维护较少:更新不频繁
  5. 文档简单:文档不够详细
  6. 测试覆盖:某些边缘情况未覆盖

10. 总结

从 go-units 这个项目可以看到几个点:

  1. Docker 生态重要:Docker 团队的库在容器场景下可复用
  2. 单位转换是刚需:大小、时间转换很常见
  3. 双向转换有价值:格式化和解析都需要
  4. 标准很重要:IEC/SI 双标准,适应不同场景
  5. 零依赖是优势:易于集成,无额外负担

go-units 解决的是单位转换这一具体问题。如果还在手动处理单位转换,可以看看它的 API 是否覆盖需求。也许有一天,Go 标准库会提供内置的单位转换功能。但在那之前,go-units 已经能承担这类工作。

参考资料

复制全文 生成海报 Go go-units Docker 单位转换 工具库

推荐文章

程序员茄子在线接单