英文:
How to compare two version number strings in golang
问题
在Go语言中,你可以使用strings.Split
函数将版本号字符串拆分为数字部分,然后逐个比较数字的大小来确定哪个版本号更大。下面是一个示例代码:
package main
import (
"fmt"
"strconv"
"strings"
)
func compareVersions(a, b string) int {
// 将版本号字符串拆分为数字部分
aParts := strings.Split(a, ".")
bParts := strings.Split(b, ".")
// 比较每个数字部分的大小
for i := 0; i < len(aParts) && i < len(bParts); i++ {
aNum, _ := strconv.Atoi(aParts[i])
bNum, _ := strconv.Atoi(bParts[i])
if aNum < bNum {
return -1
} else if aNum > bNum {
return 1
}
}
// 如果前面的数字部分都相等,则比较版本号长度
if len(aParts) < len(bParts) {
return -1
} else if len(aParts) > len(bParts) {
return 1
}
// 版本号完全相等
return 0
}
func main() {
a := "1.05.00.0156"
b := "1.0.221.9289"
result := compareVersions(a, b)
if result < 0 {
fmt.Println("Version a is smaller")
} else if result > 0 {
fmt.Println("Version a is larger")
} else {
fmt.Println("Versions are equal")
}
}
这段代码定义了一个compareVersions
函数,它接受两个版本号字符串作为参数,并返回一个整数来表示它们的大小关系。在main
函数中,我们调用compareVersions
函数来比较版本号a
和b
,并根据比较结果输出相应的信息。
注意:这段代码假设版本号字符串的格式是正确的,即由数字和点号组成。如果版本号字符串的格式不正确,可能会导致错误。你可以根据实际情况添加错误处理逻辑。
英文:
I have two strings (they are actually version numbers and they could be any version numbers)
a := "1.05.00.0156"
b := "1.0.221.9289"
I want to compare which one is bigger. How to do it in golang?
答案1
得分: 29
有一个来自Hashicorp的不错的解决方案-https://github.com/hashicorp/go-version
import github.com/hashicorp/go-version
v1, err := version.NewVersion("1.2")
v2, err := version.NewVersion("1.5+metadata")
// 比较示例。还有GreaterThan、Equal和一个简单的Compare,返回一个int,可以轻松实现>=、<=等。
if v1.LessThan(v2) {
fmt.Printf("%s 小于 %s", v1, v2)
}
英文:
There is a nice solution from Hashicorp - https://github.com/hashicorp/go-version
import github.com/hashicorp/go-version
v1, err := version.NewVersion("1.2")
v2, err := version.NewVersion("1.5+metadata")
// Comparison example. There is also GreaterThan, Equal, and just
// a simple Compare that returns an int allowing easy >=, <=, etc.
if v1.LessThan(v2) {
fmt.Printf("%s is less than %s", v1, v2)
}
答案2
得分: 13
前段时间我创建了一个版本比较库:https://github.com/mcuadros/go-version
version.CompareSimple("1.05.00.0156", "1.0.221.9289")
//返回:1
希望你喜欢!
英文:
Some time ago I created a version comparison library: https://github.com/mcuadros/go-version
version.CompareSimple("1.05.00.0156", "1.0.221.9289")
//Returns: 1
Enjoy it!
答案3
得分: 9
这是一个通用解决方案。
package main
import "fmt"
func VersionOrdinal(version string) string {
// ISO/IEC 14651:2011
const maxByte = 1<<8 - 1
vo := make([]byte, 0, len(version)+8)
j := -1
for i := 0; i < len(version); i++ {
b := version[i]
if '0' > b || b > '9' {
vo = append(vo, b)
j = -1
continue
}
if j == -1 {
vo = append(vo, 0x00)
j = len(vo) - 1
}
if vo[j] == 1 && vo[j+1] == '0' {
vo[j+1] = b
continue
}
if vo[j]+1 > maxByte {
panic("VersionOrdinal: invalid version")
}
vo = append(vo, b)
vo[j]++
}
return string(vo)
}
func main() {
versions := []struct{ a, b string }{
{"1.05.00.0156", "1.0.221.9289"},
// Go versions
{"1", "1.0.1"},
{"1.0.1", "1.0.2"},
{"1.0.2", "1.0.3"},
{"1.0.3", "1.1"},
{"1.1", "1.1.1"},
{"1.1.1", "1.1.2"},
{"1.1.2", "1.2"},
}
for _, version := range versions {
a, b := VersionOrdinal(version.a), VersionOrdinal(version.b)
switch {
case a > b:
fmt.Println(version.a, ">", version.b)
case a < b:
fmt.Println(version.a, "<", version.b)
case a == b:
fmt.Println(version.a, "=", version.b)
}
}
}
输出:
1.05.00.0156 > 1.0.221.9289
1 < 1.0.1
1.0.1 < 1.0.2
1.0.2 < 1.0.3
1.0.3 < 1.1
1.1 < 1.1.1
1.1.1 < 1.1.2
1.1.2 < 1.2
英文:
Here's a general solution.
package main
import "fmt"
func VersionOrdinal(version string) string {
// ISO/IEC 14651:2011
const maxByte = 1<<8 - 1
vo := make([]byte, 0, len(version)+8)
j := -1
for i := 0; i < len(version); i++ {
b := version[i]
if '0' > b || b > '9' {
vo = append(vo, b)
j = -1
continue
}
if j == -1 {
vo = append(vo, 0x00)
j = len(vo) - 1
}
if vo[j] == 1 && vo[j+1] == '0' {
vo[j+1] = b
continue
}
if vo[j]+1 > maxByte {
panic("VersionOrdinal: invalid version")
}
vo = append(vo, b)
vo[j]++
}
return string(vo)
}
func main() {
versions := []struct{ a, b string }{
{"1.05.00.0156", "1.0.221.9289"},
// Go versions
{"1", "1.0.1"},
{"1.0.1", "1.0.2"},
{"1.0.2", "1.0.3"},
{"1.0.3", "1.1"},
{"1.1", "1.1.1"},
{"1.1.1", "1.1.2"},
{"1.1.2", "1.2"},
}
for _, version := range versions {
a, b := VersionOrdinal(version.a), VersionOrdinal(version.b)
switch {
case a > b:
fmt.Println(version.a, ">", version.b)
case a < b:
fmt.Println(version.a, "<", version.b)
case a == b:
fmt.Println(version.a, "=", version.b)
}
}
}
Output:
1.05.00.0156 > 1.0.221.9289
1 < 1.0.1
1.0.1 < 1.0.2
1.0.2 < 1.0.3
1.0.3 < 1.1
1.1 < 1.1.1
1.1.1 < 1.1.2
1.1.2 < 1.2
答案4
得分: 4
go-semver 是一个用于 Go 语言的语义化版本库。它可以解析和比较两个语义化版本字符串。
示例:
vA := semver.New("1.2.3")
vB := semver.New("3.2.1")
fmt.Printf("%s < %s == %t\n", vA, vB, vA.LessThan(*vB))
输出:
1.2.3 < 3.2.1 == true
英文:
go-semver is a semantic versioning library for Go. It lets you parse and compare two semantic version strings.
Example:
vA := semver.New("1.2.3")
vB := semver.New("3.2.1")
fmt.Printf("%s < %s == %t\n", vA, vB, vA.LessThan(*vB))
Output:
> 1.2.3 < 3.2.1 == true
答案5
得分: 4
以下是版本比较的一些库:
- https://github.com/blang/semver
- https://github.com/Masterminds/semver
- https://github.com/hashicorp/go-version
- https://github.com/mcuadros/go-version
我使用了blang/semver库。
例如:https://play.golang.org/p/1zZvEjLSOAr
import github.com/blang/semver/v4
v1, err := semver.Make("1.0.0-beta")
v2, err := semver.Make("2.0.0-beta")
// 可用选项
v1.Compare(v2) // 比较
v1.LT(v2) // 小于
v1.GT(v2) // 大于
英文:
Here are some of the libraries for version comparison:
- https://github.com/blang/semver
- https://github.com/Masterminds/semver
- https://github.com/hashicorp/go-version
- https://github.com/mcuadros/go-version
I have used blang/semver.
Eg: https://play.golang.org/p/1zZvEjLSOAr
<!-- language: lang-go -->
import github.com/blang/semver/v4
v1, err := semver.Make("1.0.0-beta")
v2, err := semver.Make("2.0.0-beta")
// Options availabe
v1.Compare(v2) // Compare
v1.LT(v2) // LessThan
v1.GT(v2) // GreaterThan
答案6
得分: 2
这取决于你对"bigger"的理解。
一个简单的方法是:
package main
import "fmt"
import "strings"
func main() {
a := strings.Split("1.05.00.0156", ".")
b := strings.Split("1.0.221.9289", ".")
for i, s := range a {
var ai, bi int
fmt.Sscanf(s, "%d", &ai)
fmt.Sscanf(b[i], "%d", &bi)
if ai > bi {
fmt.Printf("%v 比 %v 大\n", a, b)
break
}
if bi > ai {
fmt.Printf("%v 比 %v 大\n", b, a)
break
}
}
}
你可以在这里运行这段代码:http://play.golang.org/p/j0MtFcn44Z
英文:
This depends on what you mean by bigger.
A naive approach would be:
package main
import "fmt"
import "strings"
func main() {
a := strings.Split("1.05.00.0156", ".")
b := strings.Split("1.0.221.9289", ".")
for i, s := range a {
var ai, bi int
fmt.Sscanf(s, "%d", &ai)
fmt.Sscanf(b[i], "%d", &bi)
if ai > bi {
fmt.Printf("%v is bigger than %v\n", a, b)
break
}
if bi > ai {
fmt.Printf("%v is bigger than %v\n", b, a)
break
}
}
}
答案7
得分: 2
根据Jeremy Wall的回答:
func compareVer(a, b string) (ret int) {
as := strings.Split(a, ".")
bs := strings.Split(b, ".")
loopMax := len(bs)
if len(as) > len(bs) {
loopMax = len(as)
}
for i := 0; i < loopMax; i++ {
var x, y string
if len(as) > i {
x = as[i]
}
if len(bs) > i {
y = bs[i]
}
xi,_ := strconv.Atoi(x)
yi,_ := strconv.Atoi(y)
if xi > yi {
ret = -1
} else if xi < yi {
ret = 1
}
if ret != 0 {
break
}
}
return
}
点击此处查看代码。
英文:
Based on Jeremy Wall's answer:
func compareVer(a, b string) (ret int) {
as := strings.Split(a, ".")
bs := strings.Split(b, ".")
loopMax := len(bs)
if len(as) > len(bs) {
loopMax = len(as)
}
for i := 0; i < loopMax; i++ {
var x, y string
if len(as) > i {
x = as[i]
}
if len(bs) > i {
y = bs[i]
}
xi,_ := strconv.Atoi(x)
yi,_ := strconv.Atoi(y)
if xi > yi {
ret = -1
} else if xi < yi {
ret = 1
}
if ret != 0 {
break
}
}
return
}
答案8
得分: 1
追求清晰和简洁:
func intVer(v string) (int64, error) {
sections := strings.Split(v, ".")
intVerSection := func(v string, n int) string {
if n < len(sections) {
return fmt.Sprintf("%04s", sections[n])
} else {
return "0000"
}
}
s := ""
for i := 0; i < 4; i++ {
s += intVerSection(v, i)
}
return strconv.ParseInt(s, 10, 64)
}
func main() {
a := "3.045.98.0832"
b := "087.2345"
va, _ := intVer(a)
vb, _ := intVer(b)
fmt.Println(va<vb)
}
比较版本意味着需要解析,所以我认为这两个步骤应该分开进行,以提高鲁棒性。
英文:
Striving for clarity and simplicity:
func intVer(v string) (int64, error) {
sections := strings.Split(v, ".")
intVerSection := func(v string, n int) string {
if n < len(sections) {
return fmt.Sprintf("%04s", sections[n])
} else {
return "0000"
}
}
s := ""
for i := 0; i < 4; i++ {
s += intVerSection(v, i)
}
return strconv.ParseInt(s, 10, 64)
}
func main() {
a := "3.045.98.0832"
b := "087.2345"
va, _ := intVer(a)
vb, _ := intVer(b)
fmt.Println(va<vb)
}
Comparing versions implies parsing so I believe these 2 steps should be separate to make it robust.
答案9
得分: 1
在leetcode上进行了测试:https://leetcode.com/problems/compare-version-numbers/
func compareVersion(version1 string, version2 string) int {
len1, len2, i, j := len(version1), len(version2), 0, 0
for i < len1 || j < len2 {
n1 := 0
for i < len1 && '0' <= version1[i] && version1[i] <= '9' {
n1 = n1 * 10 + int(version1[i] - '0')
i++
}
n2 := 0
for j < len2 && '0' <= version2[j] && version2[j] <= '9' {
n2 = n2 * 10 + int(version2[j] - '0')
j++
}
if n1 > n2 {
return 1
}
if n1 < n2 {
return -1
}
i, j = i+1, j+1
}
return 0
}
英文:
tested in leetcode: https://leetcode.com/problems/compare-version-numbers/
func compareVersion(version1 string, version2 string) int {
len1, len2, i, j := len(version1), len(version2), 0, 0
for i < len1 || j < len2 {
n1 := 0
for i < len1 && '0' <= version1[i] && version1[i] <= '9' {
n1 = n1 * 10 + int(version1[i] - '0')
i++
}
n2 := 0
for j < len2 && '0' <= version2[j] && version2[j] <= '9' {
n2 = n2 * 10 + int(version2[j] - '0')
j++
}
if n1 > n2 {
return 1
}
if n1 < n2 {
return -1
}
i, j = i+1, j+1
}
return 0
}
答案10
得分: 0
import (
"fmt"
"strconv"
"strings"
)
func main() {
j := ll("1.05.00.0156", "1.0.221.9289")
fmt.Println(j)
}
func ll(a, b string) int {
var length, r, l int = 0, 0, 0
v1 := strings.Split(a, ".")
v2 := strings.Split(b, ".")
len1, len2 := len(v1), len(v2)
length = len2
if len1 > len2 {
length = len1
}
for i := 0; i < length; i++ {
if i < len1 && i < len2 {
if v1[i] == v2[i] {
continue
}
}
r = 0
if i < len1 {
if number, err := strconv.Atoi(v1[i]); err == nil {
r = number
}
}
l = 0
if i < len2 {
if number, err := strconv.Atoi(v2[i]); err == nil {
l = number
}
}
if r < l {
return -1
} else if r > l {
return 1
}
}
return 0
}
这是一个用Go语言编写的程序,它定义了一个名为ll
的函数,该函数接受两个字符串参数a
和b
,并返回一个整数。在main
函数中,调用了ll
函数,并将结果打印出来。ll
函数的功能是比较两个版本号字符串的大小关系,如果a
小于b
,则返回-1;如果a
大于b
,则返回1;如果a
等于b
,则返回0。
英文:
import (
"fmt"
"strconv"
"strings"
)
func main() {
j := ll("1.05.00.0156" ,"1.0.221.9289")
fmt.Println(j)
}
func ll(a,b string) int {
var length ,r,l int = 0,0,0
v1 := strings.Split(a,".")
v2 := strings.Split(b,".")
len1, len2 := len(v1), len(v2)
length = len2
if len1 > len2 {
length = len1
}
for i:= 0;i<length;i++ {
if i < len1 && i < len2 {
if v1[i] == v2[i] {
continue
}
}
r = 0
if i < len1 {
if number, err := strconv.Atoi(v1[i]); err == nil {
r = number
}
}
l = 0
if i < len2 {
if number, err := strconv.Atoi(v2[i]); err == nil {
l = number
}
}
if r < l {
return -1
}else if r> l {
return 1
}
}
return 0
}
答案11
得分: 0
如果你可以保证版本字符串具有相同的格式(例如SemVer),你可以将其转换为整数并进行比较。这里是一个用于对SemVer切片进行排序的实现:
versions := []string{"1.0.10", "1.0.6", "1.0.9"}
sort.Slice(versions[:], func(i, j int) bool {
as := strings.Split(versions[i], ".")
bs := strings.Split(versions[j], ".")
if len(as) != len(bs) || len(as) != 3 {
return versions[i] < versions[j]
}
ais := make([]int, len(as))
bis := make([]int, len(bs))
for i := range as {
ais[i], _ = strconv.Atoi(as[i])
bis[i], _ = strconv.Atoi(bs[i])
}
//X.Y.Z
// 如果X和Y相同,比较Z
if ais[0] == bis[0] && ais[1] == bis[1] {
return ais[2] < bis[2]
}
// 如果X相同,比较Y
if ais[0] == bis[0] {
return ais[1] < bis[1]
}
// 比较X
return ais[0] < bis[0]
})
fmt.Println(versions)
英文:
If you can guarantee version strings have same format (i.e. SemVer), you can convert to int and compare int. Here is an implementation for sorting slices of SemVer:
versions := []string{"1.0.10", "1.0.6", "1.0.9"}
sort.Slice(versions[:], func(i, j int) bool {
as := strings.Split(versions[i], ".")
bs := strings.Split(versions[j], ".")
if len(as) != len(bs) || len(as) != 3 {
return versions[i] < versions[j]
}
ais := make([]int, len(as))
bis := make([]int, len(bs))
for i := range as {
ais[i], _ = strconv.Atoi(as[i])
bis[i], _ = strconv.Atoi(bs[i])
}
//X.Y.Z
// If X and Y are the same, compare Z
if ais[0] == bis[0] && ais[1] == bis[1] {
return ais[2] < bis[2]
}
// If X is same, compare Y
if ais[0] == bis[0] {
return ais[1] < bis[1]
}
// Compare X
return ais[0] < bis[0]
})
fmt.Println(versions)
答案12
得分: 0
解决方案1
这是一个版本比较函数,返回表示两个版本之间关系的符号。
// 如果 v1 > v2 返回 '>'
// 如果 v1 < v2 返回 '<'
// 否则返回 '='
func CompareVersion(v1, v2 string) byte {
v1Slice := strings.Split(v1, ".")
v2Slice := strings.Split(v2, ".")
var maxSize int
{ // 使它们具有相同的长度。
if len(v1Slice) < len(v2Slice) {
maxSize = len(v2Slice)
} else {
maxSize = len(v1Slice)
}
}
v1NSlice := make([]int, maxSize)
v2NSlice := make([]int, maxSize)
{
// 将字符串转换为整数。
for i := range v1Slice {
v1NSlice[i], _ = strconv.Atoi(v1Slice[i])
}
for i := range v2Slice {
v2NSlice[i], _ = strconv.Atoi(v2Slice[i])
}
}
var result byte
var v2Elem int
for i, v1Elem := range v1NSlice {
if result != '=' && result != 0 { // 前面的比较已经得到了答案。
return result
}
v2Elem = v2NSlice[i]
if v1Elem > v2Elem {
result = '>'
} else if v1Elem < v2Elem {
result = '<'
} else {
result = '='
}
}
return result
}
解决方案2
这是另一种更直接的方法。
CmpVer("1.01", "==", "1.001") // true
var (
ErrInvalidOperator = errors.New("invalid operator")
)
func CmpVer(v1, op, v2 string) (bool, error) {
v1Slice := strings.Split(v1, ".")
v2Slice := strings.Split(v2, ".")
var maxSize int
// 让它们具有相同的长度。
if len(v1Slice) < len(v2Slice) {
maxSize = len(v2Slice)
} else {
maxSize = len(v1Slice)
}
v1NSlice := make([]int, maxSize)
v2NSlice := make([]int, maxSize)
var err error
// 将字符串转换为整数。
for i := range v1Slice {
v1NSlice[i], err = strconv.Atoi(v1Slice[i])
if err != nil {
return false, err
}
}
for i := range v2Slice {
v2NSlice[i], err = strconv.Atoi(v2Slice[i])
if err != nil {
return false, err
}
}
var sign rune
var v2Elem int
for i, v1Elem := range v1NSlice {
if sign != '=' && sign != 0 { // 前面的比较已经得到了答案。
break
}
v2Elem = v2NSlice[i]
if v1Elem > v2Elem {
sign = '>'
} else if v1Elem < v2Elem {
sign = '<'
} else {
sign = '='
}
}
switch op {
case ">":
return sign == '>', nil
case ">=":
return sign == '>' || sign == '=', nil
case "==":
fallthrough
case "=":
return sign == '=', nil
case "!=":
return sign != '=', nil
case "<":
return sign == '<', nil
case "<=":
return sign == '<' || sign == '=', nil
default:
return false, fmt.Errorf("%w: %s", ErrInvalidOperator, op)
}
}
英文:
Solution1
This is a version comparison that returns symbols to indicate the relationship between the two versions.
// If v1 > v2 return '>'
// If v1 < v2 return '<'
// Otherwise return '='
func CompareVersion(v1, v2 string) byte {
v1Slice := strings.Split(v1, ".")
v2Slice := strings.Split(v2, ".")
var maxSize int
{ // Make them both the same size.
if len(v1Slice) < len(v2Slice) {
maxSize = len(v2Slice)
} else {
maxSize = len(v1Slice)
}
}
v1NSlice := make([]int, maxSize)
v2NSlice := make([]int, maxSize)
{
// Convert string to the int.
for i := range v1Slice {
v1NSlice[i], _ = strconv.Atoi(v1Slice[i])
}
for i := range v2Slice {
v2NSlice[i], _ = strconv.Atoi(v2Slice[i])
}
}
var result byte
var v2Elem int
for i, v1Elem := range v1NSlice {
if result != '=' && result != 0 { // The previous comparison has got the answer already.
return result
}
v2Elem = v2NSlice[i]
if v1Elem > v2Elem {
result = '>'
} else if v1Elem < v2Elem {
result = '<'
} else {
result = '='
}
}
return result
}
<kbd>go playground</kbd>
Solution2
This is another more straightforward approach.
CmpVer("1.01", "==", "1.001") // true
var (
ErrInvalidOperator = errors.New("invalid operator")
)
func CmpVer(v1, op, v2 string) (bool, error) {
v1Slice := strings.Split(v1, ".")
v2Slice := strings.Split(v2, ".")
var maxSize int
// Let each other have the same length.
if len(v1Slice) < len(v2Slice) {
maxSize = len(v2Slice)
} else {
maxSize = len(v1Slice)
}
v1NSlice := make([]int, maxSize)
v2NSlice := make([]int, maxSize)
var err error
// convert string to int
for i := range v1Slice {
v1NSlice[i], err = strconv.Atoi(v1Slice[i])
if err != nil {
return false, err
}
}
for i := range v2Slice {
v2NSlice[i], err = strconv.Atoi(v2Slice[i])
if err != nil {
return false, err
}
}
var sign rune
var v2Elem int
for i, v1Elem := range v1NSlice {
if sign != '=' && sign != 0 { // // The previous comparison has got the answer already.
break
}
v2Elem = v2NSlice[i]
if v1Elem > v2Elem {
sign = '>'
} else if v1Elem < v2Elem {
sign = '<'
} else {
sign = '='
}
}
switch op {
case ">":
return sign == '>', nil
case ">=":
return sign == '>' || sign == '=', nil
case "==":
fallthrough
case "=":
return sign == '=', nil
case "!=":
return sign != '=', nil
case "<":
return sign == '<', nil
case "<=":
return sign == '<' || sign == '=', nil
default:
return false, fmt.Errorf("%w: %s", ErrInvalidOperator, op)
}
}
<kbd>playground</kbd>
答案13
得分: -8
将"1.05.00.0156"转换为"0001"+"0005"+"0000"+"0156",然后转换为int64类型。
将"1.0.221.9289"转换为"0001"+"0000"+"0221"+"9289",然后转换为int64类型。
比较这两个int64值。
在Go playground上尝试一下。
英文:
Convert "1.05.00.0156" to "0001"+"0005"+"0000"+"0156", then to int64.
Convert "1.0.221.9289" to "0001"+"0000"+"0221"+"9289", then to int64.
Compare the two int64 values.
Try it on the <kbd>Go playground</kbd>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论