Perbandingan nomor versi dengan Python

98

Aku ingin menulis cmpfungsi -seperti yang membandingkan dua nomor versi dan kembali -1, 0atau 1berdasarkan valuses mereka dibandingkan.

  • Kembalikan -1jika versi A lebih lama dari versi B
  • Kembalikan 0jika versi A dan B setara
  • Kembalikan 1jika versi A lebih baru dari versi B

Setiap sub-bagian seharusnya ditafsirkan sebagai angka, oleh karena itu 1,10> 1,1.

Keluaran fungsi yang diinginkan adalah

mycmp('1.0', '1') == 0
mycmp('1.0.0', '1') == 0
mycmp('1', '1.0.0.1') == -1
mycmp('12.10', '11.0.0.0.0') == 1
...

Dan inilah implementasi saya, terbuka untuk perbaikan:

def mycmp(version1, version2):
    parts1 = [int(x) for x in version1.split('.')]
    parts2 = [int(x) for x in version2.split('.')]

    # fill up the shorter version with zeros ...
    lendiff = len(parts1) - len(parts2)
    if lendiff > 0:
        parts2.extend([0] * lendiff)
    elif lendiff < 0:
        parts1.extend([0] * (-lendiff))

    for i, p in enumerate(parts1):
        ret = cmp(p, parts2[i])
        if ret: return ret
    return 0

Saya menggunakan Python 2.4.5 btw. (dipasang di tempat kerja saya ...).

Berikut adalah 'rangkaian pengujian' kecil yang dapat Anda gunakan

assert mycmp('1', '2') == -1
assert mycmp('2', '1') == 1
assert mycmp('1', '1') == 0
assert mycmp('1.0', '1') == 0
assert mycmp('1', '1.000') == 0
assert mycmp('12.01', '12.1') == 0
assert mycmp('13.0.1', '13.00.02') == -1
assert mycmp('1.1.1.1', '1.1.1.1') == 0
assert mycmp('1.1.1.2', '1.1.1.1') == 1
assert mycmp('1.1.3', '1.1.3.000') == 0
assert mycmp('3.1.1.0', '3.1.2.10') == -1
assert mycmp('1.1', '1.10') == -1
Johannes Charra
sumber
Bukan jawaban tetapi saran - mungkin ada baiknya menerapkan algoritme Debian untuk perbandingan nomor versi (pada dasarnya, pengurutan bergantian bagian non-numerik dan numerik). Algoritme dijelaskan di sini (dimulai dari "String dibandingkan dari kiri ke kanan").
Hobbs
Blargh. Bagian penurunan harga yang didukung dalam komentar tidak pernah gagal untuk membingungkan saya. Tautan tetap berfungsi, meskipun terlihat bodoh.
Hobbs
Jika pembaca di masa mendatang membutuhkan ini untuk penguraian versi agen pengguna, saya merekomendasikan pustaka khusus karena variasi historisnya terlalu luas.
James Broadhead
2
Kemungkinan duplikat dari Bandingkan string versi dengan Python
John Y
1
Meskipun pertanyaan di sini lebih tua, tampaknya pertanyaan lain ini telah diurapi sebagai pertanyaan kanonik, karena banyak, banyak pertanyaan ditutup sebagai duplikat dari yang satu itu.
John Y

Jawaban:

36

Hapus bagian string yang tidak menarik (nol dan titik di belakangnya), lalu bandingkan daftar angka.

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

Ini adalah pendekatan yang sama dengan Pär Wieslander, tetapi sedikit lebih ringkas:

Berikut adalah beberapa pengujian, berkat " Bagaimana cara membandingkan dua string dalam format versi yang dipisahkan titik di 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
gnud
sumber
2
Saya khawatir itu tidak akan berhasil, itu rstrip(".0")akan mengubah ".10" menjadi ".1" di "1.0.10".
RedGlyph
Maaf, tapi dengan fungsi Anda: mycmp ('1.1', '1.10') == 0
Johannes Charra
Dengan penggunaan regex, masalah yang disebutkan di atas telah diperbaiki.
gnud
Sekarang Anda telah menggabungkan semua ide bagus dari yang lain ke dalam solusi Anda ... :-P tetap saja, inilah yang akan saya lakukan. Saya akan menerima jawaban ini. Terima kasih, semuanya
Johannes Charra
2
Catatan cmp () telah dihapus dengan Python 3: docs.python.org/3.0/whatsnew/3.0.html#ordering-comparisons
Dominic Cleal
279

Bagaimana kalau menggunakan Python distutils.version.StrictVersion?

>>> from distutils.version import StrictVersion
>>> StrictVersion('10.4.10') > StrictVersion('10.4.9')
True

Jadi untuk cmpfungsi Anda :

>>> cmp = lambda x, y: StrictVersion(x).__cmp__(y)
>>> cmp("10.4.10", "10.4.11")
-1

Jika Anda ingin membandingkan nomor versi yang lebih kompleks distutils.version.LooseVersionakan lebih berguna, namun pastikan untuk hanya membandingkan jenis yang sama.

>>> from distutils.version import LooseVersion, StrictVersion
>>> LooseVersion('1.4c3') > LooseVersion('1.3')
True
>>> LooseVersion('1.4c3') > StrictVersion('1.3')  # different types
False

LooseVersion bukanlah alat yang paling cerdas, dan dapat dengan mudah diakali:

>>> LooseVersion('1.4') > LooseVersion('1.4-rc1')
False

Untuk sukses dengan trah ini, Anda harus keluar dari perpustakaan standar dan menggunakan utilitas parsing setuptoolsparse_version .

>>> from pkg_resources import parse_version
>>> parse_version('1.4') > parse_version('1.4-rc2')
True

Jadi tergantung pada kasus penggunaan spesifik Anda, Anda harus memutuskan apakah distutilsalat bawaan sudah cukup, atau jika dijamin untuk ditambahkan sebagai ketergantungan setuptools.

bradley.ayers
sumber
2
tampaknya paling masuk akal untuk hanya menggunakan apa yang sudah ada :)
Patrick Wolf
2
Bagus! Apakah Anda mengetahui hal ini dengan membaca sumbernya? Saya tidak dapat menemukan dokumen untuk distutils.version di mana pun: - /
Adam Spiers
3
Kapan pun Anda tidak dapat menemukan dokumentasi, coba impor paket dan gunakan help ().
rspeed
13
Ketahuilah, bahwa StrictVersion HANYA berfungsi hingga versi tiga nomor. Gagal untuk hal-hal seperti 0.4.3.6!
abergmeier
6
Setiap contoh distributedalam jawaban ini harus diganti dengan setuptools, yang disertakan bersama pkg_resourcespaket dan sejak ... seperti, selamanya . Demikian juga, ini adalah dokumentasi resmi untuk pkg_resources.parse_version()fungsi yang disertakan setuptools.
Cecil Curry
30

Apakah penggunaan kembali dianggap elegan dalam hal ini? :)

# pkg_resources is in setuptools
# See http://peak.telecommunity.com/DevCenter/PkgResources#parsing-utilities
def mycmp(a, b):
    from pkg_resources import parse_version as V
    return cmp(V(a),V(b))
conny
sumber
7
Hmm, tidak begitu elegan jika Anda merujuk ke sesuatu di luar pustaka standar tanpa menjelaskan di mana mendapatkannya. Saya mengirimkan suntingan untuk memasukkan URL. Secara pribadi saya lebih suka menggunakan distutils - tampaknya tidak sepadan dengan usaha menarik perangkat lunak pihak ke-3 untuk tugas yang begitu sederhana.
Adam Spiers
1
@ adam-spires wut? Apakah Anda bahkan membaca komentarnya? pkg_resourcesadalah setuptoolspaket -bundled. Karena setuptoolssecara efektif wajib pada semua instalasi Python, pkg_resourcestersedia secara efektif di mana-mana. distutils.versionMeskipun demikian , subpaket juga berguna - meskipun kurang cerdas dibandingkan pkg_resources.parse_version()fungsi tingkat yang lebih tinggi . Yang harus Anda manfaatkan tergantung pada tingkat kegilaan yang Anda harapkan dalam string versi.
Cecil Curry
@CecilCurry Ya tentu saja saya membaca komentar (ary), itulah sebabnya saya mengeditnya untuk membuatnya lebih baik, dan kemudian menyatakan bahwa saya sudah. Agaknya Anda tidak setuju dengan pernyataan saya yang setuptoolsberada di luar perpustakaan standar, dan sebaliknya dengan preferensi yang saya nyatakan distutils dalam kasus ini . Jadi apa sebenarnya yang Anda maksud dengan "wajib secara efektif", dan tolong dapatkah Anda memberikan bukti bahwa itu "wajib secara efektif" 4,5 tahun yang lalu ketika saya menulis komentar ini?
Adam Spiers
12

Tidak perlu mengulangi tupel versi. Operator perbandingan bawaan pada list dan tuple sudah berfungsi persis seperti yang Anda inginkan. Anda hanya perlu menambah daftar versi ke nol sesuai panjangnya. Dengan python 2.6 Anda dapat menggunakan izip_longest untuk mengisi urutan.

from itertools import izip_longest
def version_cmp(v1, v2):
    parts1, parts2 = [map(int, v.split('.')) for v in [v1, v2]]
    parts1, parts2 = zip(*izip_longest(parts1, parts2, fillvalue=0))
    return cmp(parts1, parts2)

Dengan versi yang lebih rendah, beberapa peretasan peta diperlukan.

def version_cmp(v1, v2):
    parts1, parts2 = [map(int, v.split('.')) for v in [v1, v2]]
    parts1, parts2 = zip(*map(lambda p1,p2: (p1 or 0, p2 or 0), parts1, parts2))
    return cmp(parts1, parts2)
Semut Aasma
sumber
Keren, tapi sulit dimengerti untuk seseorang yang tidak bisa membaca kode seperti prosa. :) Baiklah, saya berasumsi bahwa Anda hanya dapat mempersingkat solusi dengan mengorbankan keterbacaan ...
Johannes Charra
10

Ini sedikit lebih ringkas dari saran Anda. Alih-alih mengisi versi yang lebih pendek dengan nol, saya menghapus nol yang tertinggal dari daftar versi setelah memisahkan.

def normalize_version(v):
    parts = [int(x) for x in v.split(".")]
    while parts[-1] == 0:
        parts.pop()
    return parts

def mycmp(v1, v2):
    return cmp(normalize_version(v1), normalize_version(v2))
Pär Wieslander
sumber
Bagus, terima kasih. Tapi saya masih berharap untuk satu atau dua baris ...;)
Johannes Charra
4
+1 @jellybean: dua baris tidak selalu yang terbaik untuk pemeliharaan dan keterbacaan, yang ini adalah kode yang sangat jelas dan ringkas pada saat yang sama, selain itu, Anda dapat menggunakan kembali mycmpuntuk tujuan lain dalam kode Anda jika Anda membutuhkannya.
RedGlyph
@RedGlyph: Anda benar. Seharusnya mengatakan "dua baris yang bisa dibaca". :)
Johannes Charra
hai @ Pär Wieslander, ketika saya menggunakan solusi ini untuk memecahkan masalah yang sama pada masalah Leetcode saya mendapatkan kesalahan pada loop sementara mengatakan "daftar indeks di luar jangkauan". Bisakah Anda membantu mengapa hal itu terjadi? Inilah masalahnya: leetcode.com/explore/interview/card/amazon/76/array-and-strings/…
YouHaveaBigEgo
7

Hapus trailing .0dan .00dengan regex, splitdan gunakan cmpfungsi yang membandingkan array dengan benar:

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)

Dan, tentu saja, Anda dapat mengubahnya menjadi satu baris jika Anda tidak keberatan dengan antrean yang panjang.

yu_sha
sumber
2
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('.')]))))

Ini satu liner (terbagi untuk keterbacaan). Tidak yakin tentang yang dapat dibaca ...

mavnn
sumber
1
Iya! Dan menyusut lebih jauh ( tupletidak diperlukan btw):cmp(*zip(*map(lambda x,y:(x or 0,y or 0), map(int,v1.split('.')), map(int,v2.split('.')) )))
Paul
2
from distutils.version import StrictVersion
def version_compare(v1, v2, op=None):
    _map = {
        '<': [-1],
        'lt': [-1],
        '<=': [-1, 0],
        'le': [-1, 0],
        '>': [1],
        'gt': [1],
        '>=': [1, 0],
        'ge': [1, 0],
        '==': [0],
        'eq': [0],
        '!=': [-1, 1],
        'ne': [-1, 1],
        '<>': [-1, 1]
    }
    v1 = StrictVersion(v1)
    v2 = StrictVersion(v2)
    result = cmp(v1, v2)
    if op:
        assert op in _map.keys()
        return result in _map[op]
    return result

Implementasikan untuk php version_compare, kecuali "=". Karena itu ambigu.

Ryan Fau
sumber
2

Daftar dapat dibandingkan dengan Python, jadi jika seseorang mengonversi string yang mewakili angka menjadi bilangan bulat, perbandingan Python dasar dapat digunakan dengan sukses.

Saya perlu memperluas pendekatan ini sedikit karena saya menggunakan Python3x di mana cmpfungsinya sudah tidak ada lagi. Saya harus meniru cmp(a,b)dengan (a > b) - (a < b). Dan, nomor versi sama sekali tidak bersih, dan dapat berisi semua jenis karakter alfanumerik lainnya. Ada beberapa kasus ketika fungsi tidak dapat memberi tahu pesanan sehingga ia kembali False(lihat contoh pertama).

Jadi saya memposting ini meskipun pertanyaannya sudah lama dan sudah dijawab, karena mungkin menghemat beberapa menit dalam kehidupan seseorang.

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'))
sanyi
sumber
2

Jika Anda tidak ingin menarik ketergantungan eksternal di sini adalah upaya saya yang ditulis untuk Python 3.x.

rc, rel(dan mungkin bisa ditambahkan c) dianggap sebagai "kandidat rilis" dan membagi nomor versi menjadi dua bagian dan jika hilang, nilai bagian kedua tinggi (999). Huruf lain menghasilkan perpecahan dan dibagikan sebagai sub-angka melalui kode basis-36.

import re
from itertools import chain
def compare_version(version1,version2):
    '''compares two version numbers
    >>> compare_version('1', '2') < 0
    True
    >>> compare_version('2', '1') > 0
    True
    >>> compare_version('1', '1') == 0
    True
    >>> compare_version('1.0', '1') == 0
    True
    >>> compare_version('1', '1.000') == 0
    True
    >>> compare_version('12.01', '12.1') == 0
    True
    >>> compare_version('13.0.1', '13.00.02') <0
    True
    >>> compare_version('1.1.1.1', '1.1.1.1') == 0
    True
    >>> compare_version('1.1.1.2', '1.1.1.1') >0
    True
    >>> compare_version('1.1.3', '1.1.3.000') == 0
    True
    >>> compare_version('3.1.1.0', '3.1.2.10') <0
    True
    >>> compare_version('1.1', '1.10') <0
    True
    >>> compare_version('1.1.2','1.1.2') == 0
    True
    >>> compare_version('1.1.2','1.1.1') > 0
    True
    >>> compare_version('1.2','1.1.1') > 0
    True
    >>> compare_version('1.1.1-rc2','1.1.1-rc1') > 0
    True
    >>> compare_version('1.1.1a-rc2','1.1.1a-rc1') > 0
    True
    >>> compare_version('1.1.10-rc1','1.1.1a-rc2') > 0
    True
    >>> compare_version('1.1.1a-rc2','1.1.2-rc1') < 0
    True
    >>> compare_version('1.11','1.10.9') > 0
    True
    >>> compare_version('1.4','1.4-rc1') > 0
    True
    >>> compare_version('1.4c3','1.3') > 0
    True
    >>> compare_version('2.8.7rel.2','2.8.7rel.1') > 0
    True
    >>> compare_version('2.8.7.1rel.2','2.8.7rel.1') > 0
    True

    '''
    chn = lambda x:chain.from_iterable(x)
    def split_chrs(strings,chars):
        for ch in chars:
            strings = chn( [e.split(ch) for e in strings] )
        return strings
    split_digit_char=lambda x:[s for s in re.split(r'([a-zA-Z]+)',x) if len(s)>0]
    splt = lambda x:[split_digit_char(y) for y in split_chrs([x],'.-_')]
    def pad(c1,c2,f='0'):
        while len(c1) > len(c2): c2+=[f]
        while len(c2) > len(c1): c1+=[f]
    def base_code(ints,base):
        res=0
        for i in ints:
            res=base*res+i
        return res
    ABS = lambda lst: [abs(x) for x in lst]
    def cmp(v1,v2):
        c1 = splt(v1)
        c2 = splt(v2)
        pad(c1,c2,['0'])
        for i in range(len(c1)): pad(c1[i],c2[i])
        cc1 = [int(c,36) for c in chn(c1)]
        cc2 = [int(c,36) for c in chn(c2)]
        maxint = max(ABS(cc1+cc2))+1
        return base_code(cc1,maxint) - base_code(cc2,maxint)
    v_main_1, v_sub_1 = version1,'999'
    v_main_2, v_sub_2 = version2,'999'
    try:
        v_main_1, v_sub_1 = tuple(re.split('rel|rc',version1))
    except:
        pass
    try:
        v_main_2, v_sub_2 = tuple(re.split('rel|rc',version2))
    except:
        pass
    cmp_res=[cmp(v_main_1,v_main_2),cmp(v_sub_1,v_sub_2)]
    res = base_code(cmp_res,max(ABS(cmp_res))+1)
    return res


import random
from functools import cmp_to_key
random.shuffle(versions)
versions.sort(key=cmp_to_key(compare_version))
Roland Puntaier
sumber
1

Solusi yang paling sulit untuk dibaca, tetapi tetap satu baris! dan menggunakan iterator agar cepat.

next((c for c in imap(lambda x,y:cmp(int(x or 0),int(y or 0)),
            v1.split('.'),v2.split('.')) if c), 0)

itu untuk Python2.6 dan 3. + btw, Python 2.5 dan yang lebih tua perlu menangkap StopIteration.

Paul
sumber
1

Saya melakukan ini untuk dapat mengurai dan membandingkan string versi paket Debian. Harap perhatikan bahwa itu tidak ketat dengan validasi karakter.

Ini mungkin berguna juga:

#!/usr/bin/env python

# Read <https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Version> for further informations.

class CommonVersion(object):
    def __init__(self, version_string):
        self.version_string = version_string
        self.tags = []
        self.parse()

    def parse(self):
        parts = self.version_string.split('~')
        self.version_string = parts[0]
        if len(parts) > 1:
            self.tags = parts[1:]


    def __lt__(self, other):
        if self.version_string < other.version_string:
            return True
        for index, tag in enumerate(self.tags):
            if index not in other.tags:
                return True
            if self.tags[index] < other.tags[index]:
                return True

    @staticmethod
    def create(version_string):
        return UpstreamVersion(version_string)

class UpstreamVersion(CommonVersion):
    pass

class DebianMaintainerVersion(CommonVersion):
    pass

class CompoundDebianVersion(object):
    def __init__(self, epoch, upstream_version, debian_version):
        self.epoch = epoch
        self.upstream_version = UpstreamVersion.create(upstream_version)
        self.debian_version = DebianMaintainerVersion.create(debian_version)

    @staticmethod
    def create(version_string):
        version_string = version_string.strip()
        epoch = 0
        upstream_version = None
        debian_version = '0'

        epoch_check = version_string.split(':')
        if epoch_check[0].isdigit():
            epoch = int(epoch_check[0])
            version_string = ':'.join(epoch_check[1:])
        debian_version_check = version_string.split('-')
        if len(debian_version_check) > 1:
            debian_version = debian_version_check[-1]
            version_string = '-'.join(debian_version_check[0:-1])

        upstream_version = version_string

        return CompoundDebianVersion(epoch, upstream_version, debian_version)

    def __repr__(self):
        return '{} {}'.format(self.__class__.__name__, vars(self))

    def __lt__(self, other):
        if self.epoch < other.epoch:
            return True
        if self.upstream_version < other.upstream_version:
            return True
        if self.debian_version < other.debian_version:
            return True
        return False


if __name__ == '__main__':
    def lt(a, b):
        assert(CompoundDebianVersion.create(a) < CompoundDebianVersion.create(b))

    # test epoch
    lt('1:44.5.6', '2:44.5.6')
    lt('1:44.5.6', '1:44.5.7')
    lt('1:44.5.6', '1:44.5.7')
    lt('1:44.5.6', '2:44.5.6')
    lt('  44.5.6', '1:44.5.6')

    # test upstream version (plus tags)
    lt('1.2.3~rc7',          '1.2.3')
    lt('1.2.3~rc1',          '1.2.3~rc2')
    lt('1.2.3~rc1~nightly1', '1.2.3~rc1')
    lt('1.2.3~rc1~nightly2', '1.2.3~rc1')
    lt('1.2.3~rc1~nightly1', '1.2.3~rc1~nightly2')
    lt('1.2.3~rc1~nightly1', '1.2.3~rc2~nightly1')

    # test debian maintainer version
    lt('44.5.6-lts1', '44.5.6-lts12')
    lt('44.5.6-lts1', '44.5.7-lts1')
    lt('44.5.6-lts1', '44.5.7-lts2')
    lt('44.5.6-lts1', '44.5.6-lts2')
    lt('44.5.6-lts1', '44.5.6-lts2')
    lt('44.5.6',      '44.5.6-lts1')
Pius Raeder
sumber
0

Solusi lain:

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

Seseorang juga dapat menggunakan seperti ini:

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)
pedrormjunior.dll
sumber
0

Saya menggunakan yang ini di proyek saya:

cmp(v1.split("."), v2.split(".")) >= 0
Keyrr Perino
sumber
0

Bertahun-tahun kemudian, tetapi pertanyaan ini masih ada di atas.

Ini adalah fungsi sortir versi saya. Ini membagi versi menjadi bagian angka dan non-angka. Angka dibandingkan sebagai intsisa str(sebagai bagian dari item daftar).

def sort_version_2(data):
    def key(n):
        a = re.split(r'(\d+)', n)
        a[1::2] = map(int, a[1::2])
        return a
    return sorted(data, key=lambda n: key(n))

Anda dapat menggunakan fungsi keysebagai tipe kustom Versiondengan operator pembanding. Jika benar-benar ingin menggunakan cmpAnda dapat melakukannya seperti di contoh ini: https://stackoverflow.com/a/22490617/9935708

def Version(s):
    s = re.sub(r'(\.0*)*$', '', s)  # to avoid ".0" at end
    a = re.split(r'(\d+)', s)
    a[1::2] = map(int, a[1::2])
    return a

def mycmp(a, b):
    a, b = Version(a), Version(b)
    return (a > b) - (a < b)  # DSM's answer

Lulus uji suite.

rysson
sumber
-1

Solusi pilihan saya:

Mengisi string dengan nol ekstra dan hanya menggunakan empat angka nol terlebih dahulu mudah dipahami, tidak memerlukan regex apa pun, dan lambda lebih atau kurang dapat dibaca. Saya menggunakan dua baris agar mudah dibaca, bagi saya keanggunan itu pendek dan sederhana.

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))
daramarak
sumber
-1

Ini solusi saya (ditulis dalam C, maaf). Saya harap Anda akan merasakan manfaatnya

int compare_versions(const char *s1, const char *s2) {
    while(*s1 && *s2) {
        if(isdigit(*s1) && isdigit(*s2)) {
            /* compare as two decimal integers */
            int s1_i = strtol(s1, &s1, 10);
            int s2_i = strtol(s2, &s2, 10);

            if(s1_i != s2_i) return s1_i - s2_i;
        } else {
            /* compare as two strings */
            while(*s1 && !isdigit(*s1) && *s2 == *s1) {
                s1++;
                s2++;
            }

            int s1_i = isdigit(*s1) ? 0 : *s1;
            int s2_i = isdigit(*s2) ? 0 : *s2;

            if(s1_i != s2_i) return s1_i - s2_i;
        }
    }

    return 0;
}
e_asphyx
sumber