目录

前言

在某些场景,我们可能需要对版本号进行排序。版本号的形式有很多种,例如:

  • 1.0.0, 1.0.1.1, 2.0.1.1
  • v1.0.0, v1.10.1, v2.0
  • ······

而本文所介绍的版本号排序工具,是针对有规律的数字版本号如 1.0.0, 1, 2.15.0 这种形式。

创作解读

版本号的大小比较与排序

版本号排序的前提,首先得比较两个版本号的大小。由于版本号长度可能不一致,所以需要额外做一些处理。对于版本号的比较,我的算法思路是:

."1.0"["1", "0"]"1.0.1"["1", "0", "1"]
firstVersions := strings.Split(versions[i], ".")
secondVersions := strings.Split(versions[j], ".")
填充0,统一长度["1", "0"]["1", "0", "1"]["1", "0", "0"]
// 0 填充
// 获取最大长度并向最小长度的切片填充 "0",统一长度
func getMaxAndFillZero(s1 *[]string, s2 *[]string) int {
    len1, len2 := len(*s1), len(*s2)
    if len1 > len2 {
        fillZero(s2, len1-len2)
        return len1
    }
    fillZero(s1, len2-len1)
    return len2
}

// 0 填充
func fillZero(s *[]string, size int) {
    for i := 0; i < size; i++ {
        *s = append(*s, "0")
    }
}
size0

3、遍历切片,从前依次比较两个版本号每段数字的大小。

如果第一个版本号的第一段数字大于或小于第二个版本号的第二段数字,则可以根据排序规则决定两个版本号的先后位置。

如果相等,则比较下一段数字的大小,以此类推。

for k := 0; k < maxLen; k++ {
    // 由于上面判断了版本号的合法性,因此 error 可以忽略
    vi, _ := strconv.Atoi(firstVersions[k])
    vj, _ := strconv.Atoi(secondVersions[k])
    if vi < vj {
        if sortRule == DESC {
            // 降序排序
            // todo 交换操作
        }
        // 默认升序排序,即使 sortRule 不是 ASC
        // todo 交换操作
    } else if vi > vj {
        // 降序排序
        if sortRule == DESC {
            // todo 交换操作
        }
        // 默认升序排序,即使 sortRule 不是 ASC
        // todo 交换操作
    }
}
SliceStable(x any, less func(i, j int) bool)
sort.SliceStable(versions, func(i, j int) bool {
    firstVersions := strings.Split(versions[i], ".")
    secondVersions := strings.Split(versions[j], ".")
    // 判断版本号格式的合法性
    isNormal(firstVersions)
    isNormal(secondVersions)
    // 获取最大值并填充 "0", 统一长度
    maxLen := getMaxAndFillZero(&firstVersions, &secondVersions)
    for k := 0; k < maxLen; k++ {
        // 由于上面判断了版本号的合法性,因此 error 可以忽略
        vi, _ := strconv.Atoi(firstVersions[k])
        vj, _ := strconv.Atoi(secondVersions[k])
        if vi < vj {
            if sortRule == DESC {
                // 降序排序
                return false
            }
            // 默认升序排序,即使 sortRule 不是 ASC
            return true
        } else if vi > vj {
            // 降序排序
            if sortRule == DESC {
                return true
            }
            // 默认升序排序,即使 sortRule 不是 ASC
            return false
        }
    }
    return false
})

版本号的合法性校验

由于本工具处理的版本号是有规律的数字版本号,如果版本号包含字母或其他特殊字符,会影响到排序的进行,因此需要提前对版本号进行合法性的校验。

// 判断版本号的格式是否合法
func isNormal(versions []string) {
   for _, v := range versions {
      for _, r := range []rune(v) {
         if !unicode.IsNumber(r) {
            panic(errors.New("版本号格式错误:" + string(r)))
         }
      }
   }
}
panic

错误处理

由于版本号的不合法性,可能会程序运行的过程中产生错误。因此,有必要人工捕获错误,提高工具的健壮性。

error
defer func() {
   if r := recover(); r != nil {
      if er, ok := r.(error); ok {
         err = er
      } else {
         err = errors.New("")
         fmt.Println("未知错误: ")
         fmt.Println(r)
      }
   }
}()
panic

总结

0panic
您可能感兴趣的文章: