版本號之間比較大小,本質上是比較字符串之間的關系孵睬。這里給定兩個版本號播歼,你一定能迅速地區(qū)分出大小:
0.0.2
0.0.3
想要讓計算機程序分辨她們的關系掰读,可以直接使用strcmp()
函數(shù)獲取返回值秘狞,如果你在 iOS 系統(tǒng)下編程,也可以使用 NSString 類提供的- (NSComparisonResult)compare:(NSNumber *)decimalNumber
方法蹈集。
但如果版本進化成下面這樣烁试,上面所說的 API 一定會產生錯誤的結果。
2.7.14.2345
2.12.8.1234
因為單純的字符串比較大小的依據(jù)是每個字符的 ASCII 碼拢肆。程序會認為字符7
比字符1
大减响,結果就是第一個版本號大于第二個版本號靖诗,這是錯誤的。
因此在識別之前支示,有必要了解一下版本號的組成部分刊橘,以及每一個數(shù)字的意義,這里已 GUN 風格的版本號作為說明颂鸿。
版本號大致以下幾個部分組成:
- 主版本號
- 次版本號
- 修正版本號
- 編譯版本號
示例: 3.5.20.9527
在比較版本號時伤为,正確的做法應該是,主版本號和主版本號比較据途,次版本號和次版本號比較等等绞愚。所以算法的核心應該是分離出版本號的各個組成部分。這個檢測算法用 Swift 來做非常簡單颖医,只需要利用數(shù)組的 lexicographicallyPrecedes 方法即可實現(xiàn)位衩。相關代碼可查閱該項目OhMyVersion。
當然 Objective-C 語言里并沒有 Array 的這個方法熔萧,那我們就自己動手實現(xiàn)一個糖驴。以下是一個簡單的實現(xiàn):
@implementation NSArray (Compare)
- (BOOL)lexicographicallyPrecedesWithOther:(NSArray *)array areInIncreasingOrder:(BOOL (^)(id, id))block
{
NSParameterAssert(array);
NSParameterAssert(block);
NSUInteger idx = 0;
for (id obj in self) {
if (idx == array.count) { break; }
if ([obj isEqual:array[idx]]) { ++idx; continue; }
return block(obj, array[idx]);
}
return self.count > array.count;
}
@end
下面我也列舉了粗暴的 C 語言算法原理:
/**
* 比較版本號
*
* @param v1 第一個版本號
* @param v2 第二個版本號
*
* @return 如果版本號相等,返回 0,
* 如果第一個版本號低于第二個佛致,返回 -1贮缕,否則返回 1.
*/
int compareVersion(const char *v1, const char *v2)
{
assert(v1);
assert(v2);
const char *p_v1 = v1;
const char *p_v2 = v2;
while (*p_v1 && *p_v2) {
char buf_v1[32] = {0};
char buf_v2[32] = {0};
char *i_v1 = strchr(p_v1, '.');
char *i_v2 = strchr(p_v2, '.');
if (!i_v1 || !i_v2) break;
if (i_v1 != p_v1) {
strncpy(buf_v1, p_v1, i_v1 - p_v1);
p_v1 = i_v1;
}
else
p_v1++;
if (i_v2 != p_v2) {
strncpy(buf_v2, p_v2, i_v2 - p_v2);
p_v2 = i_v2;
}
else
p_v2++;
int order = atoi(buf_v1) - atoi(buf_v2);
if (order != 0)
return order < 0 ? -1 : 1;
}
double res = atof(p_v1) - atof(p_v2);
if (res < 0) return -1;
if (res > 0) return 1;
return 0;
}
Test case:
int main(int argc, char *argv[])
{
assert(compare_version("2.2.1", "2.2.0") > 0);
assert(compare_version("2.2.1", "2.1.9") > 0);
assert(compare_version("2.2.1", "2.2.01") == 0);
assert(compare_version("2.2.1", "2.2.1") == 0);
assert(compare_version("2.2", "2.1.1") > 0);
assert(compare_version("2.2", "2.2.1") < 0);
assert(compare_version("2.2.3.1", "2.2.3.5") < 0);
assert(compare_version("2.2.3.1", "2.2.3.0") > 0);
assert(compare_version("2.2", "2.2.1.4.5") < 0);
assert(compare_version("2.2.3.4", "2.2.4.4.5") < 0);
assert(compare_version("2.2.3.4.5.6", "2.2.3.4.5.12") < 0);
assert(compare_version("2.2.3.4.5.6", "2.2.2.4.5.12") > 0);
assert(compare_version("3.0.0.1", "3.0.0.0.1") > 0);
assert(compare_version("3.1", "3.1.") == 0);
puts("test pass.");
}