Version number comparison in Python

后端 未结 17 2016
小蘑菇
小蘑菇 2020-11-27 10:32

I want to write a cmp-like function which compares two version numbers and returns -1, 0, or 1 based on their compared va

相关标签:
17条回答
  • 2020-11-27 11:21

    Another solution:

    def mycmp(v1, v2):
        import itertools as it
        f = lambda v: list(it.dropwhile(lambda x: x == 0, map(int, v.split('.'))[::-1]))[::-1]
        return cmp(f(v1), f(v2))
    

    One can use like this too:

    import itertools as it
    f = lambda v: list(it.dropwhile(lambda x: x == 0, map(int, v.split('.'))[::-1]))[::-1]
    f(v1) <  f(v2)
    f(v1) == f(v2)
    f(v1) >  f(v2)
    
    0 讨论(0)
  • 2020-11-27 11:22

    Remove trailing .0 and .00 with regex, split and use cmp function which compares arrays correctly:

    def mycmp(v1,v2):
     c1=map(int,re.sub('(\.0+)+\Z','',v1).split('.'))
     c2=map(int,re.sub('(\.0+)+\Z','',v2).split('.'))
     return cmp(c1,c2)
    

    And, of course, you can convert it to a one-liner if you don't mind the long lines.

    0 讨论(0)
  • 2020-11-27 11:27

    Remove the uninteresting part of the string (trailing zeroes and dots), and then compare the lists of numbers.

    import re
    
    def mycmp(version1, version2):
        def normalize(v):
            return [int(x) for x in re.sub(r'(\.0+)*$','', v).split(".")]
        return cmp(normalize(version1), normalize(version2))
    

    This is the same approach as Pär Wieslander, but a bit more compact:

    Here are some tests, thanks to "How to compare two strings in dot separated version format in Bash?":

    assert mycmp("1", "1") == 0
    assert mycmp("2.1", "2.2") < 0
    assert mycmp("3.0.4.10", "3.0.4.2") > 0
    assert mycmp("4.08", "4.08.01") < 0
    assert mycmp("3.2.1.9.8144", "3.2") > 0
    assert mycmp("3.2", "3.2.1.9.8144") < 0
    assert mycmp("1.2", "2.1") < 0
    assert mycmp("2.1", "1.2") > 0
    assert mycmp("5.6.7", "5.6.7") == 0
    assert mycmp("1.01.1", "1.1.1") == 0
    assert mycmp("1.1.1", "1.01.1") == 0
    assert mycmp("1", "1.0") == 0
    assert mycmp("1.0", "1") == 0
    assert mycmp("1.0", "1.0.1") < 0
    assert mycmp("1.0.1", "1.0") > 0
    assert mycmp("1.0.2.0", "1.0.2") == 0
    
    0 讨论(0)
  • 2020-11-27 11:27
    def compare_version(v1, v2):
        return cmp(*tuple(zip(*map(lambda x, y: (x or 0, y or 0), 
               [int(x) for x in v1.split('.')], [int(y) for y in v2.split('.')]))))
    

    It's a one liner (split for legability). Not sure about readable...

    0 讨论(0)
  • 2020-11-27 11:30

    Lists are comparable in Python, so if someone converts the strings representing the numbers into integers, the basic Python comparison can be used with success.

    I needed to extend this approach a bit because I use Python3x where the cmp function does not exist any more. I had to emulate cmp(a,b) with (a > b) - (a < b). And, version numbers are not that clean at all, and can contain all kind of other alphanumeric characters. There are cases when the function can't tell the order so it returns False (see the first example).

    So I'm posting this even if the question is old and answered already, because it may save a few minutes in someone's life.

    import re
    
    def _preprocess(v, separator, ignorecase):
        if ignorecase: v = v.lower()
        return [int(x) if x.isdigit() else [int(y) if y.isdigit() else y for y in re.findall("\d+|[a-zA-Z]+", x)] for x in v.split(separator)]
    
    def compare(a, b, separator = '.', ignorecase = True):
        a = _preprocess(a, separator, ignorecase)
        b = _preprocess(b, separator, ignorecase)
        try:
            return (a > b) - (a < b)
        except:
            return False
    
    print(compare('1.0', 'beta13'))    
    print(compare('1.1.2', '1.1.2'))
    print(compare('1.2.2', '1.1.2'))
    print(compare('1.1.beta1', '1.1.beta2'))
    
    0 讨论(0)
  • 2020-11-27 11:30

    My preferred solution:

    Padding the string with extra zeroes and just using the four first is easy to understand, doesn't require any regex and the lambda is more or less readable. I use two lines for readability, for me elegance is short and simple.

    def mycmp(version1,version2):
      tup = lambda x: [int(y) for y in (x+'.0.0.0.0').split('.')][:4]
      return cmp(tup(version1),tup(version2))
    
    0 讨论(0)
提交回复
热议问题