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") } }