Go语言实现有规律的数字版本号的排序工具

目录
  • 前言
  • 创作解读
    • 版本号的大小比较与排序
    • 版本号的合法性校验
    • 错误处理
  • 总结

前言

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

  • 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、以 . 为分隔符,将版本号的每段数字存到切片里,方便后续比较大小。例如 "1.0"["1", "0"]"1.0.1"["1", "0", "1"]

firstVersions := strings.Split(versions[i], ".")
secondVersions := strings.Split(versions[j], ".")

2、由于两个版本号的长度可能不一致,因此需要做 填充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")
    }
}

size 为最大长度 - 最小长度的值,也就是要填充 0 的个数。

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 的错误,并结束排序。

总结

  • 本工具实现了对有规律的数字版本号集合进行排序。
  • 在排序的过程中,由于版本号的长度可能不一致,因此执行填充 0 操作,统一长度,再进行版本号的大小比较;
  • 除此之外,还对版本号的合法性做了校验,捕获可预知和不可预知的 panic 错误,提高了工具的健壮性。
  • 经测试,核心功能已实现,但有些地方还能改进,后续会对代码进行优化。

到此这篇关于Go语言实现有规律的数字版本号的排序工具的文章就介绍到这了,更多相关Go语言排序工具内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Go归并排序算法的实现方法

    目录 归并排序的思想 归并排序的 Go 代码实现 归并排序的时间复杂度 今天继续基础排序算法的图解和Go 代码实现,这次分享一个时间复杂度为*** 诶,时间复杂度多少先保密,文末会有分析.这次分享的排序算法是—归并排序(Merge Sort). 归并排序的思想 与快速排序一样,归并排序采用的也是分治的策略,把原本的问题先分解成一些小问题进行求解,再把这些小问题各自的答案修整到一起得到原本问题的答案,从而达到分而治之的目的. 归并排序算法会把要排序的序列分成长度相当的两个子序列,当分无可分每个子序

  • Go语言实现常用排序算法的示例代码

    目录 冒泡排序 快速排序 选择排序 插入排序 排序算法是在生活中随处可见,也是算法基础,因为其实现代码较短,应用较常见.所以在面试中经常会问到排序算法及其相关的问题,可以说是每个程序员都必须得掌握的了.为了方便大家学习,花了一天的时间用Go语言实现一下常用的算法且整理了一下,如有需要可以参考. 冒泡排序 思路:从前往后对相邻的两个元素依次进行比较,让较大的数往下沉,较小的网上冒,即每当两个相邻的元素比较后发现他们的排序要求相反时,就将它们互换. 时间复杂度:O(N^2) 空间复杂度:O(1) f

  • Go 数据结构之堆排序示例详解

    目录 堆排序 堆排序过程 动画显示 开始堆排序 代码实现 总结 堆排序 堆排序是一种树形选择排序算法. 简单选择排序算法每次选择一个关键字最小的记录需要 O(n) 的时间,而堆排序选择一个关键字最小的记录需要 O(nlogn)的时间. 堆可以看作一棵完全二叉树的顺序存储结构. 在这棵完全二叉树中,如果每个节点的值都大于等于左边孩子的值,称为大根堆(最大堆.又叫大顶堆).如果每个节点的值都小于等于左边孩子的值,称为小根堆(最小堆,小顶堆). 可以,用数学符号表示如下: 堆排序过程 构建初始堆 在输

  • 详解go语言中sort如何排序

    目录 sort包源码解读 前言 如何使用 基本数据类型切片的排序 自定义Less排序比较器 自定义数据结构的排序 分析下源码 不稳定排序 稳定排序 查找 Interface 总结 参考 sort 包源码解读 前言 我们的代码业务中很多地方需要我们自己进行排序操作,go 标准库中是提供了 sort 包是实现排序功能的,这里来看下生产级别的排序功能是如何实现的. go version go1.16.13 darwin/amd64 如何使用 先来看下 sort 提供的主要功能 对基本数据类型切片的排序

  • Golang中堆排序的实现

    堆排序 堆的概念: 堆是一棵基于数组实现的特殊的完全二叉树,这棵二叉树的每个节点的值必须大于或小于它的两个子节点.大顶堆是每个节点的值必须大于它的两个子节点,小顶堆则相反. 堆的顶点必定是ta的最大值或最小值 堆在数组中的存储形式: 满足完全二叉树的情况下,数组中的每个元素依次插入堆中.如图: 堆[9,8,9,8,7,6,4,1,2,0]的存储形式是这样的 堆的性质: 假定数组nums的长度为leng 堆的最后一个节点的父节点下标为:leng/2-1 任何一个下标为n的节点的左右子节点下标为:左

  • Go语言实现有规律的数字版本号的排序工具

    目录 前言 创作解读 版本号的大小比较与排序 版本号的合法性校验 错误处理 总结 前言 在某些场景,我们可能需要对版本号进行排序.版本号的形式有很多种,例如: 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 这种形式. 创作解读 版本号的大小比较与排序 版本号排序的前提,首先得比较两个版本号的大小.由于版本号长度可能不一致,所以需要额外做一些处理.

  • C语言实现将字符串转换为数字的方法

    本文实例讲述了C语言实现将字符串转换为数字的方法.分享给大家供大家参考.具体实现方法如下: C语言提供了几个标准库函数,可以将字符串转换为任意类型(整型.长整型.浮点型等)的数字.以下是用atoi()函数将字符串转换为整数的一个例子: 复制代码 代码如下: # include <stdio. h> # include <stdlib. h> void main (void) ; void main (void) {     int num;     char * str = &qu

  • C语言 数组中重复的数字分析及方法

    C语言 数组中重复的数字解决方法: 题目:在一个长度为n的数组里的所有数字都在0-n-1的 范围内.数组中某些数字是重复的,但是不知道有几个数字重复了,也不知道每个数字重复了几次.请找出数组中任意一个重复的数字.例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是重复的数字2或者3. 解法1:对于数组进行排序,之后对于已经排序的数组进行遍历便可知道数组中重复的数字. 时间复杂度;O(nlogn); 解法2:建立一个大小为O(N)的哈希表,遍历数组中的元素并判断是否存在于哈

  • C语言实现将字符和数字串到一起

    C 语言 是相对比较低级的一种语言,所有的数据都是按照 整数.浮点数.数组.指针 一类的数据结构来表示的. 相比之下,较高级的语言中 数据类型之间的转换就更自由一些. 毕竟,不同的语言有不同的设计目的. 在 C 语言中,将 整数 或者其它类型的元类数据串到字符串中,可以使用 sprintf 函数. sprintf 函数的第一个参数是输出的字符串,第二个是一个 Format String,格式描述.在其中插入 %d %g 一类的百分号标记 来插入数据. 用sprintf char szBuff[1

  • C语言实现输入两个数字将其按从小到大输出的方法

    代码实例: 1.第一种方法(if) #include <stdio.h> int main() { int a , b; printf("请输入两个整数:"); scanf("%d%d",&a , &b); if(a > b){ int temp; temp = a; a = b; b = temp; printf("%d < %d \n", a , b); } } 源代码图片: 运行结果截图说明: 到此这

  • C语言实现简单的猜数字游戏

    本文实例为大家分享了C语言实现简单的猜数字游戏的具体代码,供大家参考,具体内容如下 1.游戏介绍 此游戏是电脑随机生成一个1-99的数字(生成数字范围可自行更改),然后玩家一直猜数字,最终猜对为止.废话不多说,直接上完整代码. 2.游戏完整代码 #include<stdio.h> #include<time.h> #include<stdlib.h>   int juge(int guess,int number) {     int t=0;     if(guess

  • C语言超详细讲解猜数字游戏的实现

    目录 rand函数 srand函数 时间戳 完整代码与程序运行图 rand函数 先看一下下面这张图: 通过这张图我们可以了解到rand函数的返回值是int类型,形参为空,它的头文件<stdlib.h>以及它的返回值范围是0到RAND_MAX. 而RAND_MAX在VS中是0x7fff,转换成二进制就是32767.所以rand返回的随机数的范围就是0到32727. 其实我们用rand函数就可以生成随机数了,那为什么我们还要用到srand函数呢? 因为只有第一次生成的随机数是随机的,如果程序执行多

  • C语言详解实现猜数字游戏步骤

    目录 前言 一.猜数字游戏是什么 二.使用步骤 总结 前言 本文详细介绍了猜数字游戏的具体实现逻辑 一.猜数字游戏是什么 1.电脑自动生成一个1-100以内的数字 2.我们输入一个我们猜的数字 3.如果我们猜的数字比电脑随机生成的数字大,那么输出(猜大了) 4.如果我们猜的数字比电脑随机生成的数字小,那么输出(猜小了) 5.如此循环往复,猜到正确的数字之后游戏结束 二.使用步骤 1.首先应该打印菜单 代码如下 void menu() { printf("*********************

  • Go语言题解LeetCode268丢失的数字示例详解

    目录 题目描述 思路分析 AC 代码 异或两遍 - 丢失的数字 解题思路 代码 C++ 排序二分.加减法.异或 - 丢失的数字 解题思路: 题目描述 原题链接 : 268. 丢失的数字 给定一个包含 [0, n] 中 n 个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数. 示例 1: 输入:nums = [3,0,1] 输出:2 解释:n = 3,因为有 3 个数字,所以所有的数字都在范围 [0,3] 内.2 是丢失的数字,因为它没有出现在 nums 中. 示例 2

  • 浅谈Go语言不提供隐式数字转换的原因

    什么是隐式转换 在 C 语言中,隐式数字转换是指在某些情况下,编译器会自动将一个数据类型转换为另一个数据类型,而不需要明确地进行类型转换操作. 以下是一些常见的隐式数字转换规则: 当一个整数类型和一个浮点数类型进行运算时,整数类型会被自动转换为浮点数类型. 当一个表达式中包含两种不同类型的整数类型时,小范围的整数类型会被自动转换为大范围的整数类型. 当一个表达式中包含两种不同类型的浮点数类型时,精度较低的浮点数类型会被自动转换为精度较高的浮点数类型. 以下是一个使用隐式数字转换的 C 语言代码示

随机推荐