OpenCloud 项目中的 go-humanize:Go 语言人性化数字、字节大小与相对时间格式化实战指南
2026/9/19 1:44:23 网站建设 项目流程

OpenCloud 项目中的 go-humanize:Go 语言人性化数字、字节大小与相对时间格式化实战指南

【免费下载链接】opencloud🌤️ OpenCloud is the open source platform for file management, sharing and collaboration. Simple and sovereign.项目地址: https://gitcode.com/GitHub_Trending/op/opencloud

github.com/dustin/go-humanize是一个轻量级的 Go 工具库,提供一组"把机器友好的数值变成人类友好字符串"的函数:字节大小(83 MB/79 MiB)、相对时间(7 hours ago)、千分位逗号(1,000,000,000)、序数(193rd)以及英文复数等。本文以该库在本仓库中的 vendored 源码(vendor/github.com/dustin/go-humanize)为事实依据,完整讲解其全部 API、默认行为、底层实现原理与典型使用场景,帮助你在自己的 Go 服务中正确选用这些格式化工具。

库定位与在本仓库中的依赖形态

go-humanize 的核心设计理念正如其 README 所言:"Just a few functions for helping humanize times and sizes."它不依赖任何第三方运行库,纯标准库实现,适合嵌入各类 Go 服务。

在本仓库中,它以indirect 依赖的形式被 vendor 管理:go.mod第 196 行声明github.com/dustin/go-humanize v1.0.1 // indirect,对应go.sum中的校验记录;源码被完整拷贝到 vendor/github.com/dustin/go-humanize/ 目录下,包含:

  • bytes.go/bigbytes.go:字节大小格式化与解析
  • times.go:相对时间格式化
  • comma.go/commaf.go:千分位数字
  • ftoa.go:浮点数格式化
  • si.go:SI 记数法
  • ordinals.go:序数
  • number.go/big.go:通用数字工具
  • english/:英文复数与词序列子包

若要在自己的项目中使用,标准做法是go get github.com/dustin/go-humanize,导入路径为"github.com/dustin/go-humanize",包别名统一为humanize

Sizes:字节大小的 SI / IEC 双轨格式化

这是该库最常用的能力:把82854982这样的裸字节数变成易读的字符串。

fmt.Printf("That file is %s.", humanize.Bytes(82854982)) // That file is 83 MB.

两套进制体系:BytesIBytes

源码(bytes.go)中同时定义了两种常量体系,使用时按业务约定选择:

体系函数进制常量输出示例(82854982)
SI(十进制)Bytes1000KByte=1000、MByte=1000² …EByte83 MB
IEC(二进制)IBytes1024KiByte=1024、MiByte=1024² …EiByte79 MiB

Bytes的源码实现非常直白:sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"},配合humanateBytes内部用对数logn(s, base)求数量级、四舍五入到一位小数(小于 10 时保留一位小数,否则取整)输出。存储介质厂商标称容量通常用 SI(Bytes),操作系统与文件系统通常用 IEC(IBytes),两者混用是磁盘容量"缩水"争议的根源,格式化时务必明确采用哪套标准。

反向解析:ParseBytes

Bytes/IBytes的反向操作是ParseBytes,可把人类可读字符串解析回字节数:

// ParseBytes("42 MB") -> 42000000, nil // ParseBytes("42 mib") -> 44040192, nil

从源码看,它的解析策略是:先扫描字符串头部的数字(允许小数点与逗号,逗号会被剥离),再对剩余后缀做小写化、去空格后查bytesSizeTable映射表。该映射表同时收录了完整后缀("kib"/"kb"等)、缩写后缀("ki"/"k"等)和空后缀(裸字节),因此"42 mib""42 Mi""42"都能正确解析;后缀不识别时返回unhandled size name错误,结果溢出uint64时返回too large错误。

大数支持

当数值超过uint64范围时,可使用bigbytes.go中基于math/bigBigBytes/BigIBytes,以及big.go中的BigComma等大数版本,保证任意精度下依然输出规范格式。

Times:相对时间的智能分级

Time接受一个time.Time,返回"距现在"的人性化描述:

fmt.Printf("This was touched %s.", humanize.Time(someTimeInstance)) // This was touched 7 hours ago.

过去的时间自动追加ago,未来的时间自动追加from now。其实现(times.go)定义了一组时间单位常量:Day = 24 * time.HourWeek = 7 * DayMonth = 30 * DayYear = 12 * MonthLongTime = 37 * Year——注意这里的月按 30 天、年按 12 个月折算,是近似值而非日历概念

默认分级表(defaultMagnitudes)

Time的底层是RelTime+CustomRelTime,核心是一张"相对时间分级表"。每个RelTimeMagnitude结构由三部分组成:D(切换阈值时长)、Format(含%s标签位与%d数值位的格式串)、DivBy(显示数值的除数)。默认分级表如下,顺序严格递增:

D(阈值)输出格式DivBy
1 秒now1 秒
2 秒1 second %s1
1 分钟%d seconds %s1 秒
2 分钟1 minute %s1
1 小时%d minutes %s1 分钟
2 小时1 hour %s1
1 天%d hours %s1 小时
2 天1 day %s1
1 周%d days %s1 天
2 周1 week %s1
1 月%d weeks %s1 周
2 月1 month %s1
1 年%d months %s1 月
18 月1 year %s1
2 年2 years %s1
37 年%d years %s1 年
MaxInt64a long while %s1

实现上,CustomRelTime先计算两时间点差值,用二分查找(sort.Search)定位到第一个D > diff的分级,随后解析 Format 串中的%s/%d占位符完成替换。得益于这套机制,你也可以传入自定义分级表,完全定制输出粒度。

自定义相对时间:RelTime/CustomRelTime

  • RelTime(a, b, "earlier", "later"):以ab两个时刻比较,较早者用第一个标签;
  • CustomRelTime(a, b, albl, blbl, magnitudes):额外传入自定义分级表,适合"几分钟前/几小时前"等产品化需求。

Ordinals:序数后缀

来自 golang-nuts 邮件列表讨论的经典需求:把数字转成带序数后缀的字符串。

0 -> 0th 1 -> 1st 2 -> 2nd 3 -> 3rd 4 -> 4th [...]
fmt.Printf("You're my %s best friend.", humanize.Ordinal(193)) // You are my 193rd best friend.

实现位于 ordinals.go,按英文序数规则处理个位与十位的特殊情况(如 11/12/13 一律为th),可直接用于榜单、排名、楼层号等文案拼接。

Commas:千分位逗号

Commaint64数字每三位插入逗号,并正确处理符号:

0 -> 0 100 -> 100 1000 -> 1,000 1000000000 -> 1,000,000,000 -100000 -> -100,000
fmt.Printf("You owe $%s.\n", humanize.Comma(6582491)) // You owe $6,582,491.

从 comma.go 源码可见两个值得注意的细节:

  • math.MinInt64特判:最小负整数无法安全取反,源码直接返回硬编码的-9,223,372,036,854,775,808
  • 负号单独提取,主体按三位一组切分并做前导零补齐后以逗号连接。

浮点与超大数版本

  • Commaf(v float64):浮点版本(commaf.go),如Commaf(834142.32) -> 834,142.32,整数部分加逗号、小数部分原样保留;
  • CommafWithDigits(f, decimals):在Commaf基础上限制小数位数,如CommafWithDigits(834142.32, 1) -> 834,142.3
  • BigComma(b *big.Int):基于math/big的大整数版本(big.go),循环DivMod千位分组,避免int64溢出。

Ftoa:更"干净"的浮点数输出

fmt.Printf("%f", 2.24)会输出2.240000,而humanize.Ftoa会去除多余的尾随零:

fmt.Printf("%f", 2.24) // 2.240000 fmt.Printf("%s", humanize.Ftoa(2.24)) // 2.24 fmt.Printf("%f", 2.0) // 2.000000 fmt.Printf("%s", humanize.Ftoa(2.0)) // 2

实现位于 ftoa.go,适合展示配置项数值、统计指标等不希望出现2.240000的界面场景。

SI notation:SI 记数法格式化

SI函数把数字按国际单位制前缀(nano/micro/milli/kilo 等)格式化:

humanize.SI(0.00000000223, "M") // 2.23 nM

实现位于 si.go。它接收一个基准单位字符串,根据数量级自动选择合适的前缀——例如上述示例以M为单位(即每 1e6 个单位进一位),0.00000000223 M就表示2.23 nM。这类函数适合科学计算、传感器数据、性能指标等跨度极大的数值展示。

english 子包:英文复数与词序列

以下函数位于humanize/english子包,导入路径为github.com/dustin/go-humanize/english,用于英文文案的本地化拼接。

Plurals:复数

english.PluralWord(1, "object", "") // object english.PluralWord(42, "object", "") // objects english.PluralWord(2, "bus", "") // buses english.PluralWord(99, "locus", "loci") // loci english.Plural(1, "object", "") // 1 object english.Plural(42, "object", "") // 42 objects english.Plural(2, "bus", "") // 2 buses english.Plural(99, "locus", "loci") // 99 loci
  • PluralWord(count, singular, plural):只返回单词形态(内部带bus -> buses等规则化处理);
  • Plural(count, singular, plural):返回数字 + 空格 + 单词的完整短语;
  • 第三个参数用于提供不规则复数(如locus的复数是loci),留空则走规则推导。

Word series:逗号词列表

把字符串切片拼接成英文惯用的逗号列表:

english.WordSeries([]string{"foo"}, "and") // foo english.WordSeries([]string{"foo", "bar"}, "and") // foo and bar english.WordSeries([]string{"foo", "bar", "baz"}, "and") // foo, bar and baz english.OxfordWordSeries([]string{"foo", "bar", "baz"}, "and") // foo, bar, and baz

区别在于OxfordWordSeries遵循牛津逗号规范,在最后一个词前额外加逗号(foo, bar, and baz),适用于通知、权限说明等多元素枚举文案。

在本仓库中如何进一步研读源码

go-humanize 的全部源码、许可证与文档都随 vendor 机制保留在本仓库中,可直接在 vendor/github.com/dustin/go-humanize/ 目录下按需查阅:

  • 字节格式化与解析的完整常量表、解析错误处理:bytes.go
  • 相对时间分级表与二分查找实现:times.go
  • 千分位逗号的MinInt64特判与分组算法:comma.go、commaf.go
  • 依赖声明与版本锁定:go.mod第 196 行(v1.0.1 // indirect)及go.sum对应哈希

小结

go-humanize 的实用之处在于把三类最常被机器格式化"毁掉可读性"的数据——字节大小、时间差、大数字——用十几行函数收敛成统一、可测试的格式化入口。实际使用时只需记住几条原则:存储容量选IBytes、厂商容量选Bytes;时间展示用Time、需要自定义粒度用CustomRelTime;金融或统计数字用Comma/Commaf系列;英文文案用english子包。它的实现全部基于标准库,无外部依赖,在 OpenCloud 这类大型 Go 服务中作为 indirect 依赖被 vendor 管理,也正是看中了它小而稳、可审计的特点。

【免费下载链接】opencloud🌤️ OpenCloud is the open source platform for file management, sharing and collaboration. Simple and sovereign.项目地址: https://gitcode.com/GitHub_Trending/op/opencloud

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询