如何比較版本號--Python實現

昀溪發表於2018-08-17

需求

在寫一個程式Django專案的setup程式(初始化環境,比如設定PIP源,安裝該專案依賴的各種模組等操作)遇到一個系統當前模組版本和專案所需版本的比較然後給出建議是忽略還是升級。我的要求是不僅僅比較版本號是否一致以及返回最大版本號,而且還要給出建議是升級(當前系統包的版本號小於專案需要的版本號)還是忽略(當前系統包的版本號大於等於專案需要的版本號)。下圖就是我們要去比較的東西。

解題分析

  • 版本號雖然是數字組成但是一個整體的版本號無法透過數字進行比較需要拆解逐位比較
  • 版本號有長度的區別我們可以稱作位,比如1.1.1這就是3位,我們這裡的位說的是“.”分割的位數。 1.111.1也是三位。位數就是“.”的個數加1. 

演算法一:補位演算法

比如1.11.2和1.9比較,既然是逐位比較那就需要迴圈,兩個版本號位數不同如果按照最長的位數迴圈那短的哪一個肯定拋異常,那麼我就可以透過補位來實現,怎麼補呢?1.9 和 1.9.0 看到了麼,這兩個版本號是相同的,為什麼呢?有誰見過1位的版本號呢?都使用1.0而不會單純使用1,長度相同那剩下的就是迴圈比較。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys

def checkVersion(currentversion, expectedversion):
    """
    檢查版本號是否相同,當前版本小於期望版本則進行升級,大於等於則忽略。
    CODE有兩種,88和99 表示建議,88表示當前版本大於等於期待的版本忽略,99表示小於期待版本要進行升級。
    :param currentversion: 當前系統存在的版本號
    :param expectedversion: 期望的版本號
    :return: CODE, MAX_VERSION
    """

    MAX_VERSION = "0.0.0"
    CODE = 88

    # 如果兩者一樣就直接返回,用於加快處理速度。 這裡不要用 is 要用 == 來比較內容是否一致。
    if expectedversion == currentversion:
        CODE = 88
        MAX_VERSION = expectedversion
        return CODE, MAX_VERSION

    # 切割成列表
    currentversionBITS = currentversion.split(".")
    expectedversionBITS = expectedversion.split(".")

    """
    為了避免版本號長度不同比如  1.0.8和1.2 我們把版本號要補全都變成相同長度,比如 1.2.0 這樣比較的時候迴圈次數相同
    """
    if len(currentversionBITS) >= len(expectedversionBITS):
        amount = len(currentversionBITS) - len(expectedversionBITS)
        for i in range(amount):
            expectedversionBITS.append("0")
    else:
        amount = len(expectedversionBITS) - len(currentversionBITS)
        for i in range(amount):
            currentversionBITS.append("0")

    """
    逐位比較版本大小,為什麼這裡採用currentversionBITS的長度來迴圈呢,其實講過上面的if語句後無論是currentversionBITS還是expectedversionBITS
    位數都相同,這裡採用那個長度來控制迴圈都可以。
    """
    for i in range(len(currentversionBITS)):
        try:
            if int(currentversionBITS[i]) > int(expectedversionBITS[i]):
                CODE = 88
                MAX_VERSION = currentversion
                return CODE, MAX_VERSION
            elif int(currentversionBITS[i]) < int(expectedversionBITS[i]):
                CODE = 99
                MAX_VERSION = expectedversion
                return CODE, MAX_VERSION
            else:
                CODE = 88
                MAX_VERSION = expectedversion
        except IndexError as err:
            pass

    return CODE, MAX_VERSION

def main():
    print checkVersion("1.0", "1.1")  # 正確的返回 99 1.1
    print checkVersion("1.1.2", "1.1")  # 正確的返回 88 1.1.2
    print checkVersion("1.11.3", "1.11.24")  # 正確的返回 99 1.11.24
    print checkVersion("1.0.11.3", "1.0.11.2")  # 正確的返回 88 1.0.11.3

if __name__ == "__main__":
    try:
        main()
    finally:
        sys.exit()

執行結果

演算法二:不補位按最小長度迴圈

不補位就要安裝最小位數長度迴圈,否則就會丟擲異常,當然我們可以做異常處理,不過執行邏輯還是不要放在異常裡因為異常會影響效能,再說明明這個異常可以避免幹嘛還要讓它出現呢。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys

def checkVersionG2(currentversion, expectedversion):
    """
    檢查版本號是否相同,當前版本小於期望版本則進行升級,大於等於則忽略.本方法是基於第一種方法的改進,效能更好,程式碼更少。
    CODE有兩種,88和99 表示建議,88表示當前版本大於等於期待的版本忽略,99表示小於期待版本要進行升級。
    :param currentversion: 當前系統存在的版本號
    :param expectedversion: 期望的版本號
    :return: CODE, MAX_VERSION
    """

    MAX_VERSION = "0.0.0"

    # 切割成列表
    currentversionBITS = currentversion.split(".")
    expectedversionBITS = expectedversion.split(".")

    """
    找出2個版本號位數最小的一個,注意這裡就存在資料互換問題,經過這個if語句之後,你就完全不知道 minbitversion和maxbitversion
    分別對應的是currentversion還是expectedversion.
    """
    if len(currentversionBITS) >= len(expectedversionBITS):
        minbitversion = expectedversionBITS
        maxbitversion = currentversionBITS
    else:
        minbitversion = currentversionBITS
        maxbitversion = expectedversionBITS

    """
    逐位比較版本大小,按最小位迴圈,這個迴圈之後將會找出版本號最大的,而且這裡必須讓這個迴圈完成。這裡和之前的演算法不同。
    再多說一句,這裡你也可以按照最大位迴圈,但是你就需要考慮異常,從程式效能角度來說盡量不要使用異常控制邏輯,除非沒有更好的選擇。
    """
    for index, bit in enumerate(minbitversion):
        try:
            if int(bit) > int(maxbitversion[index]):
                MAX_VERSION = ".".join(minbitversion)
                break
            elif int(bit) < int(maxbitversion[index]):
                MAX_VERSION = ".".join(maxbitversion)
                break
            else:
                MAX_VERSION = ".".join(maxbitversion)
        except IndexError as err:
            pass

    # 這裡則用於找到當前的 MAX_VERSION 到底是currentversion還是expectedversion,只有找到了才能給出建議是忽略還是升級。
    # 之前那種演算法裡沒有,因為透過位數補全之後比較不存在一段資料不清狀態。
    if MAX_VERSION == currentversion:
        CODE = 88
    else:
        CODE = 99

    return CODE, MAX_VERSION


def main():
    print checkVersionG2("1.0", "1.1")  # 正確的返回 99 1.1
    print checkVersionG2("1.1.2", "1.1")  # 正確的返回 88 1.1.2
    print checkVersionG2("1.11.3", "1.11.24")  # 正確的返回 99 1.11.24
    print checkVersionG2("1.0.11.3", "1.0.11.2")  # 正確的返回 88 1.0.11.3

if __name__ == "__main__":
    try:
        main()
    finally:
        sys.exit()

執行結果

總結

上面只是用了Python的實現,演算法通用。這兩個方法裡面沒有對版本號格式的檢驗簡易增加,透過正則就可以。

相關文章