• Go 完整实现版本比较 VersionCompare 函数


    【转】 http://www.syyong.com/Go/Go-implementation-version-comparison-VersionCompare-function.html

    VersionCompare — 用于对比两个的版本数字字符串大小。

    此函数首先在版本字符串里用一个点 . 替换 _、- 和 +,也会在任意非数字前后插入一个点 .,这样,类似 '4.3.2RC1' 将会变成 '4.3.2.RC.1'。 接下来它会分割结果,然后它会从左往右对比各个部分。如果某部分包含了特定的版本字符串,将会用以下顺序处理:列表中未找到的任意字符串 < dev < alpha = a < beta = b < RC = rc < # < pl = p。 这种方式不仅能够对比类似 '4.1' 和 '4.1.2' 那种不同的版本级别,同时也可以指定对比任何包含开发状态的版本。

    VersionCompare

    // version_compare()
    // The possible operators are: <, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne respectively.
    // special version strings these are handled in the following order,
    // (any string not found) < dev < alpha = a < beta = b < RC = rc < # < pl = p
    // Usage:
    // VersionCompare("1.2.3-alpha", "1.2.3RC7", '>=')
    // VersionCompare("1.2.3-beta", "1.2.3pl", 'lt')
    // VersionCompare("1.1_dev", "1.2any", 'eq')
    func VersionCompare(version1, version2, operator string) bool {
        var vcompare func(string, string) int
        var canonicalize func(string) string
        var special func(string, string) int
    
        // version compare
        vcompare = func(origV1, origV2 string) int {
            if origV1 == "" || origV2 == "" {
                if origV1 == "" && origV2 == "" {
                    return 0
                } else {
                    if origV1 == "" {
                        return -1
                    } else {
                        return 1
                    }
                }
            }
    
            ver1, ver2, compare := "", "", 0
            if origV1[0] == '#' {
                ver1 = origV1
            } else {
                ver1 = canonicalize(origV1)
            }
            if origV2[0] == '#' {
                ver2 = origV2
            } else {
                ver2 = canonicalize(origV2)
            }
            n1, n2 := 0, 0
            for {
                p1, p2 := "", ""
                n1 = strings.IndexByte(ver1, '.')
                if n1 == -1 {
                    p1, ver1 = ver1, ""
                } else {
                    p1, ver1 = ver1[:n1], ver1[n1+1:]
                }
                n2 = strings.IndexByte(ver2, '.')
                if n2 == -1 {
                    p2, ver2 = ver2, ""
                } else {
                    p2, ver2 = ver2[:n2], ver2[n2+1:]
                }
                if (p1[0] >= '0' && p1[0] <= '9') && (p2[0] >= '0' && p2[0] <= '9') { // all isdigit
                    l1, _ := strconv.Atoi(p1)
                    l2, _ := strconv.Atoi(p2)
                    if l1 > l2 {
                        compare = 1
                    } else if l1 == l2 {
                        compare = 0
                    } else {
                        compare = -1
                    }
                } else if !(p1[0] >= '0' && p1[0] <= '9') && !(p2[0] >= '0' && p2[0] <= '9') { // all isndigit
                    compare = special(p1, p2)
                } else { // part isdigit
                    if p1[0] >= '0' && p1[0] <= '9' { // isdigit
                        compare = special("#N#", p2)
                    } else {
                        compare = special(p1, "#N#")
                    }
                }
                if compare != 0 || n1 == -1 || n2 == -1 {
                    break
                }
            }
    
            if compare == 0 {
                if ver1 != "" {
                    if ver1[0] >= '0' && ver1[0] <= '9' {
                        compare = 1
                    } else {
                        compare = vcompare(ver1, "#N#")
                    }
                } else if ver2 != "" {
                    if ver2[0] >= '0' && ver2[0] <= '9' {
                        compare = -1
                    } else {
                        compare = vcompare("#N#", ver2)
                    }
                }
            }
    
            return compare
        }
    
        // canonicalize
        canonicalize = func(version string) string {
            ver := []byte(version)
            l := len(ver)
            if l == 0 {
                return ""
            }
            var buf = make([]byte, l*2)
            j := 0
            for i, v := range ver {
                next := uint8(0)
                if i+1 < l { // Have the next one
                    next = ver[i+1]
                }
                if v == '-' || v == '_' || v == '+' { // repalce "-","_","+" to "."
                    if j > 0 && buf[j-1] != '.' {
                        buf[j] = '.'
                        j++
                    }
                } else if (next > 0) &&
                    (!(next >= '0' && next <= '9') && (v >= '0' && v <= '9')) ||
                    (!(v >= '0' && v <= '9') && (next >= '0' && next <= '9')) { // Insert '.' before and after a non-digit
                    buf[j] = v
                    j++
                    if v != '.' && next != '.' {
                        buf[j] = '.'
                        j++
                    }
                    continue
                } else if !((v >= '0' && v <= '9') ||
                    (v >= 'a' && v <= 'z') || (v >= 'A' && v <= 'Z')) { // Non-letters and numbers
                    if j > 0 && buf[j-1] != '.' {
                        buf[j] = '.'
                        j++
                    }
                } else {
                    buf[j] = v
                    j++
                }
            }
    
            return string(buf[:j])
        }
    
        //compare special version forms
        special = func(form1, form2 string) int {
            found1, found2, len1, len2 := -1, -1, len(form1), len(form2)
            // (Any string not found) < dev < alpha = a < beta = b < RC = rc < # < pl = p
            forms := map[string]int{
                "dev":   0,
                "alpha": 1,
                "a":     1,
                "beta":  2,
                "b":     2,
                "RC":    3,
                "rc":    3,
                "#":     4,
                "pl":    5,
                "p":     5,
            }
    
            for name, order := range forms {
                if len1 < len(name) {
                    continue
                }
                if strings.Compare(form1[:len(name)], name) == 0 {
                    found1 = order
                    break
                }
            }
            for name, order := range forms {
                if len2 < len(name) {
                    continue
                }
                if strings.Compare(form2[:len(name)], name) == 0 {
                    found2 = order
                    break
                }
            }
    
            if found1 == found2 {
                return 0
            } else if found1 > found2 {
                return 1
            } else {
                return -1
            }
        }
    
        compare := vcompare(version1, version2)
    
        switch operator {
        case "<", "lt":
            return compare == -1
        case "<=", "le":
            return compare != 1
        case ">", "gt":
            return compare == 1
        case ">=", "ge":
            return compare != -1
        case "==", "=", "eq":
            return compare == 0
        case "!=", "<>", "ne":
            return compare != 0
        default:
            panic("operator: invalid")
        }
    }

    Github地址

    https://github.com/syyongx/php2go

  • 相关阅读:
    FreeMarker MyEclipse IDE
    Mybatis MapperScannerConfigurer 自动扫描 将Mapper接口生成代理注入到Spring
    Mybatis SqlSessionTemplate 源码解析
    Mybatis Interceptor 拦截器原理 源码分析
    MyBatis Mapper 接口如何通过JDK动态代理来包装SqlSession 源码分析
    MyEclipse SVN 下面切换用户的解决方案
    SpringMVC 多个数据源 配置多个事物管理器 Multiple Transaction Managers
    Activiti 获取定义
    [No000066]python各种类型转换-int,str,char,float,ord,hex,oct等
    [No000065]python 获取当前时间
  • 原文地址:https://www.cnblogs.com/syyong/p/8940874.html
Copyright © 2020-2023  润新知