package utils import ( "fmt" "regexp" "strconv" "strings" ) type StorageCapacityUnit uint64 func (u StorageCapacityUnit) String() string { switch u { case Byte: return "Byte" case KB: return "KB" case MB: return "MB" case GB: return "GB" case TB: return "TB" case PB: return "PB" case KiB: return "KiB" case MiB: return "MiB" case GiB: return "GiB" case TiB: return "TiB" case PiB: return "PiB" } return "unknow unit" } const ( Byte StorageCapacityUnit = 1 KB StorageCapacityUnit = Byte * 1000 MB StorageCapacityUnit = KB * 1000 GB StorageCapacityUnit = MB * 1000 TB StorageCapacityUnit = GB * 1000 PB StorageCapacityUnit = TB * 1000 KiB StorageCapacityUnit = 1 << 10 MiB StorageCapacityUnit = 1 << 20 GiB StorageCapacityUnit = 1 << 30 TiB StorageCapacityUnit = 1 << 40 PiB StorageCapacityUnit = 1 << 50 ) var ( ReStorageSize = regexp.MustCompile(`^([1-9][0-9]*)((?:[KMGTPkmgtp]|)i?[bB])$`) ReUnit = regexp.MustCompile(`^([kmgtpKMGTP]|)(|i)([bB])$`) ) // MemorySize 内存大小,Num代表数字,Unit代表单位 type MemorySize struct { Num uint64 Unit StorageCapacityUnit } // HumanReadStr 显示人类可读性的字符串,参数i表示精度 func (s MemorySize) HumanReadStr(i int) string { if s.Num == 0 { return fmt.Sprintf("0%s", s.Unit) } total := s.Num * uint64(s.Unit) units := []StorageCapacityUnit{Byte, KiB, MiB, GiB, TiB, PiB} var target StorageCapacityUnit for k, v := range units { if total/uint64(v) < 1 { target = units[k-1] break } else { target = v } } num := float64(total) / float64(target) switch i { case 0: return fmt.Sprintf("%d%s", int(num), target) case 1: return fmt.Sprintf("%.1f%s", num, target) case 2: return fmt.Sprintf("%.2f%s", num, target) case 3: return fmt.Sprintf("%.3f%s", num, target) default: return fmt.Sprintf("%.3f%s", num, target) } } func ParseUnit(s string) (StorageCapacityUnit, error) { s = strings.Trim(strings.TrimSpace(s), "\n") s = strings.ReplaceAll(s, " ", "") if !ReUnit.MatchString(s) { return 0, fmt.Errorf("invalid storage size unit: %s", s) } s = strings.ToLower(s) // [MiB M i B] // [KB K '' B] // [B '' '' B] matchs := ReUnit.FindStringSubmatch(s) if len(matchs) < 4 { return 0, fmt.Errorf("invalid storage size unit: %s", s) } isI := matchs[2] == "i" switch matchs[1] { case "": return Byte, nil case "k": if isI { return KiB, nil } return KB, nil case "m": if isI { return MiB, nil } return MB, nil case "g": if isI { return GiB, nil } return GB, nil case "t": if isI { return TiB, nil } return TB, nil case "p": if isI { return PiB, nil } return PB, nil default: return 0, fmt.Errorf("invalid storage size unit: %s", s) } } // ParseMemorySize 解析容量字符串,支持的格式有:123MiB 123MB "123 MiB" "123 MB" func ParseMemorySize(s string) (*MemorySize, error) { s = strings.TrimSpace(strings.Trim(s, " \n")) s = strings.ReplaceAll(s, " ", "") if !ReStorageSize.MatchString(s) { return nil, fmt.Errorf("invalid memory size format: %s", s) } matchs := ReStorageSize.FindStringSubmatch(s) if len(matchs) < 3 { return nil, fmt.Errorf("invalid memory size format: %s", s) } num, err := strconv.ParseUint(matchs[1], 10, 64) if err != nil { return nil, err } result := MemorySize{} result.Num = num unit, err := ParseUnit(matchs[2]) if err != nil { return nil, err } result.Unit = unit return &result, nil }