版本号大小比较算法

版本号之间比较大小,本质上是比较字符串之间的关系。这里给定两个版本号,你一定能迅速地区分出大小:

0.0.2
0.0.3

想要让计算机程序分辨她们的关系,可以直接使用strcmp()函数获取返回值,如果你在 iOS 系统下编程,也可以使用 NSString 类提供的- (NSComparisonResult)compare:(NSNumber *)decimalNumber方法。

但如果版本进化成下面这样,上面所说的 API 一定会产生错误的结果。

2.7.14.2345
2.12.8.1234

因为单纯的字符串比较大小的依据是每个字符的 ASCII 码。程序会认为字符7比字符1大,结果就是第一个版本号大于第二个版本号,这是错误的。

因此在识别之前,有必要了解一下版本号的组成部分,以及每一个数字的意义,这里已 GUN 风格的版本号作为说明。

版本号大致以下几个部分组成:

  • 主版本号
  • 次版本号
  • 修正版本号
  • 编译版本号

示例: 3.5.20.9527

在比较版本号时,正确的做法应该是,主版本号和主版本号比较,次版本号和次版本号比较等等。所以算法的核心应该是分离出版本号的各个组成部分。这个检测算法用 Swift 来做非常简单,只需要利用数组的 lexicographicallyPrecedes 方法即可实现。相关代码可查阅该项目OhMyVersion

当然 Objective-C 语言里并没有 Array 的这个方法,那我们就自己动手实现一个。以下是一个简单的实现:

@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.");
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,928评论 6 509
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,748评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,282评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,065评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,101评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,855评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,521评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,414评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,931评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,053评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,191评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,873评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,529评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,074评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,188评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,491评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,173评论 2 357

推荐阅读更多精彩内容

  • 第5章 引用类型(返回首页) 本章内容 使用对象 创建并操作数组 理解基本的JavaScript类型 使用基本类型...
    大学一百阅读 3,237评论 0 4
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,673评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,213评论 25 707
  • 嗨AV8D,我又回来了。 你们过得怎么样啊?昨天我小正经了一把,说实话还挺怀念这个不正经的自己。 (www.jia...
    格斯墨阅读 916评论 45 40
  • Selector参数 Swift3.0版本 #selector 事件监听本质: 将方法包装成@SEL -> 类中查...
    Gary_fei阅读 198评论 0 1