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.两套进制体系:Bytes与IBytes
源码(bytes.go)中同时定义了两种常量体系,使用时按业务约定选择:
| 体系 | 函数 | 进制 | 常量 | 输出示例(82854982) |
|---|---|---|---|---|
| SI(十进制) | Bytes | 1000 | KByte=1000、MByte=1000² …EByte | 83 MB |
| IEC(二进制) | IBytes | 1024 | KiByte=1024、MiByte=1024² …EiByte | 79 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/big的BigBytes/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.Hour、Week = 7 * Day、Month = 30 * Day、Year = 12 * Month、LongTime = 37 * Year——注意这里的月按 30 天、年按 12 个月折算,是近似值而非日历概念。
默认分级表(defaultMagnitudes)
Time的底层是RelTime+CustomRelTime,核心是一张"相对时间分级表"。每个RelTimeMagnitude结构由三部分组成:D(切换阈值时长)、Format(含%s标签位与%d数值位的格式串)、DivBy(显示数值的除数)。默认分级表如下,顺序严格递增:
| D(阈值) | 输出格式 | DivBy |
|---|---|---|
| 1 秒 | now | 1 秒 |
| 2 秒 | 1 second %s | 1 |
| 1 分钟 | %d seconds %s | 1 秒 |
| 2 分钟 | 1 minute %s | 1 |
| 1 小时 | %d minutes %s | 1 分钟 |
| 2 小时 | 1 hour %s | 1 |
| 1 天 | %d hours %s | 1 小时 |
| 2 天 | 1 day %s | 1 |
| 1 周 | %d days %s | 1 天 |
| 2 周 | 1 week %s | 1 |
| 1 月 | %d weeks %s | 1 周 |
| 2 月 | 1 month %s | 1 |
| 1 年 | %d months %s | 1 月 |
| 18 月 | 1 year %s | 1 |
| 2 年 | 2 years %s | 1 |
| 37 年 | %d years %s | 1 年 |
| MaxInt64 | a long while %s | 1 |
实现上,CustomRelTime先计算两时间点差值,用二分查找(sort.Search)定位到第一个D > diff的分级,随后解析 Format 串中的%s/%d占位符完成替换。得益于这套机制,你也可以传入自定义分级表,完全定制输出粒度。
自定义相对时间:RelTime/CustomRelTime
RelTime(a, b, "earlier", "later"):以a、b两个时刻比较,较早者用第一个标签;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:千分位逗号
Comma为int64数字每三位插入逗号,并正确处理符号:
0 -> 0 100 -> 100 1000 -> 1,000 1000000000 -> 1,000,000,000 -100000 -> -100,000fmt.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 lociPluralWord(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),仅供参考