应先用 strings.TrimRightFunc 或字符扫描提取数值部分,再用 strconv.ParseFloat 解析,最后根据标准化单位(如 KiB=1024)换算为字节;需区分 SI(1000)与 IEC(1024)前缀,显式处理大小写和“i”,并校验溢出与非法输入。

用 strconv.ParseFloat 提取数字,再手动处理单位后缀
Go 标准库没有内置函数直接解析 "1.5MB" 或 "2G" 这类带单位的字符串。最稳妥的做法是:先用正则或字符串切分提取数值部分,再单独识别单位并换算。别依赖第三方包——多数场景下几行代码就能搞定,且避免引入不必要依赖和单位歧义(比如 "K" 到底是 1024 还是 1000)。
常见错误是直接用 strconv.ParseFloat 去解析整个字符串,结果报错 "invalid syntax",因为 "1.5MB" 不是合法浮点字面量。
- 用
strings.TrimRightFunc(s, unicode.IsLetter)快速剥离末尾字母(适用于简单情况,如"1024KiB") - 更健壮的做法:用正则
^([0-9.]+)([KMGTPEZYkmgtpezy]?i?B?)$捕获数值和单位,注意大小写和可选的i(如KiB) - 单位换算统一按二进制前缀(IEC 标准):
KiB = 1024,MiB = 1024²;若需十进制(SI),则用K = 1000,M = 1000²,需明确业务约定
ParseFileSize 函数建议实现逻辑
封装一个函数比每次手写解析更可靠。核心逻辑分三步:提取数字、标准化单位字符串、查表换算。不要在单位映射里写死 map[string]uint64 然后遍历——用 switch 更快也更清晰。
示例关键片段:
立即学习“go语言免费学习笔记(深入)”;
func ParseFileSize(s string) (uint64, error) {
numStr := strings.TrimRightFunc(s, unicode.IsLetter)
num, err := strconv.ParseFloat(numStr, 64)
if err != nil {
return 0, err
}
unit := strings.TrimLeftFunc(s, func(r rune) bool { return unicode.IsDigit(r) || r == '.' })
<pre class="brush:php;toolbar:false;">switch strings.ToUpper(unit) {
case "B", "":
return uint64(num), nil
case "K", "KI", "KIB":
return uint64(num * 1024), nil
case "M", "MI", "MIB":
return uint64(num * 1024 * 1024), nil
case "G", "GI", "GIB":
return uint64(num * 1024 * 1024 * 1024), nil
default:
return 0, fmt.Errorf("unsupported unit: %q", unit)
}}
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
注意:strings.ToUpper(unit) 后要匹配 "KIB" 而非 "KiB",否则 "kib" 会漏掉;"B" 和空字符串都视为字节,兼容 "123" 这种无单位输入。
单位大小写和 i 的语义必须显式区分
很多人忽略这点:Linux ls -lh 输出的是 KiB(带 i),而某些 CLI 工具或配置文件可能用 KB 表示 1000 字节。Go 里不做假设,必须由调用方明确单位含义,或在函数文档里写清默认行为。
-
"KB"→ 默认按 SI(1000),除非你业务强制要求 IEC(1024) -
"KiB"或"KIB"→ 明确走 1024 路径 - 小写
k、m等通常不被接受,应统一转大写后再判断,避免"mb"匹配失败 - 不支持复合单位(如
"MB/s"),那是带速率的场景,应另写解析器
性能敏感时避免正则,改用字符扫描
如果这个解析会在高频路径(如日志解析、HTTP header 处理)中被反复调用,正则编译和匹配开销明显。此时应手写单次遍历:从左到右读取数字(支持小数点),遇到第一个非数字非点字符就停,剩余部分作为单位。
这样既无内存分配(不用 strings.Split),也不触发正则引擎。实测比正则快 3–5 倍,且代码行数差不多。
容易踩的坑是没处理负号(文件大小不可能为负,但若输入非法,应早失败)、没跳过开头空格(" 1.2MiB")、或把 e 当作单位("1e6" 是合法浮点数,不能截成 "1" + "e6")。
单位识别环节最易出错的地方,其实是边界情况:空字符串、只有单位("GB")、数值溢出("99999999999999999999GB")。这些得靠 math.MaxUint64 检查,而不是全信 ParseFloat 的结果。

















