【问题标题】:Compare version numbers in Objective-C比较 Objective-C 中的版本号
【发布时间】:2009-12-30 04:40:15
【问题描述】:

我正在编写一个应用程序来接收包含项目和版本号的数据。这些数字的格式类似于“1.0.1”或“1.2.5”。如何比较这些版本号?我认为它们必须首先格式化为字符串,不是吗?我有哪些选项可以确定“1.2.5”在“1.0.1”之后?

【问题讨论】:

  • 我写了那个小库来轻松比较 Obj-C 中的 2 个版本的字符串。通常在 iOS 中。有例子和代码在GitHub page
  • 它有助于准确地阐明版本控制方案是什么。有些格式可能需要额外的逻辑。

标签: objective-c comparison


【解决方案1】:

这是比较版本的最简单方法,记住 "1"

NSString* requiredVersion = @"1.2.0";
NSString* actualVersion = @"1.1.5";

if ([requiredVersion compare:actualVersion options:NSNumericSearch] == NSOrderedDescending) {
  // actualVersion is lower than the requiredVersion
}

【讨论】:

  • 我正在使用这种方法,我最近发现它在比较时返回(我认为的)错误结果,即:2.4.06 和 2.4.4。我认为 2.4.06 应该低于 2.4.4,但也许我错了……有什么想法吗?
  • @Omer:为什么是 06 而不是 6?我想大多数开发者会认为 2.4.06 是比 2.4.4 更高的版本。
  • 这很好很简单,但依赖于一个非常简单的版本方案。
  • @ScottBerrevoets 我当然希望这不是它的工作方式,因为这意味着“1.2.3”小于“1.1.12”(123 Numbers within strings are compared using numeric value”。也就是说,将比较字符串中的数字集(本质上是componentsSeparatedByString 方法)。您可以使用 @"1.8"@"1.7.2.3.55" 自己测试一下,看看 1.8 会提前出现。
  • NSNumericSearch 认为“1.0”小于“1.0.0”。对于我的目的来说不够灵活。
【解决方案2】:

我将添加我的方法,它将严格的数字版本(无 a、b、RC 等)与任意数量的组件进行比较。

+ (NSComparisonResult)compareVersion:(NSString*)versionOne toVersion:(NSString*)versionTwo {
    NSArray* versionOneComp = [versionOne componentsSeparatedByString:@"."];
    NSArray* versionTwoComp = [versionTwo componentsSeparatedByString:@"."];

    NSInteger pos = 0;

    while ([versionOneComp count] > pos || [versionTwoComp count] > pos) {
        NSInteger v1 = [versionOneComp count] > pos ? [[versionOneComp objectAtIndex:pos] integerValue] : 0;
        NSInteger v2 = [versionTwoComp count] > pos ? [[versionTwoComp objectAtIndex:pos] integerValue] : 0;
        if (v1 < v2) {
            return NSOrderedAscending;
        }
        else if (v1 > v2) {
            return NSOrderedDescending;
        }
        pos++;
    }

    return NSOrderedSame;
}

【讨论】:

    【解决方案3】:

    这是对 Nathan de Vries 答案的扩展,用于解决 1

    首先,我们可以使用NSString 类别解决版本字符串中多余“.0”的问题:

    @implementation NSString (VersionNumbers)
    - (NSString *)shortenedVersionNumberString {
        static NSString *const unnecessaryVersionSuffix = @".0";
        NSString *shortenedVersionNumber = self;
    
        while ([shortenedVersionNumber hasSuffix:unnecessaryVersionSuffix]) {
            shortenedVersionNumber = [shortenedVersionNumber substringToIndex:shortenedVersionNumber.length - unnecessaryVersionSuffix.length];
        }
    
        return shortenedVersionNumber;
    }
    @end
    

    使用上述NSString 类别,我们可以缩短版本号以删除不必要的.0

    NSString* requiredVersion = @"1.2.0";
    NSString* actualVersion = @"1.1.5";
    
    requiredVersion = [requiredVersion shortenedVersionNumberString]; // now 1.2
    actualVersion = [actualVersion shortenedVersionNumberString]; // still 1.1.5
    

    现在我们仍然可以使用 Nathan de Vries 提出的精美简单的方法:

    if ([requiredVersion compare:actualVersion options:NSNumericSearch] == NSOrderedDescending) {
      // actualVersion is lower than the requiredVersion
    }
    

    【讨论】:

    • 这与 Nathan de Vries 解决方案相结合是最好和最优雅的答案。
    • 这不还是说7.4.2比7.5高吗?
    • @Tres 没有。因为 NSNumericSearch 作为选项传入,所以字符串作为数字进行比较,因此 7.4.2
    【解决方案4】:

    我自己做的,使用类别..

    来源..

    @implementation NSString (VersionComparison)
    - (NSComparisonResult)compareVersion:(NSString *)version{
        NSArray *version1 = [self componentsSeparatedByString:@"."];
        NSArray *version2 = [version componentsSeparatedByString:@"."];
        for(int i = 0 ; i < version1.count || i < version2.count; i++){
            NSInteger value1 = 0;
            NSInteger value2 = 0;
            if(i < version1.count){
                value1 = [version1[i] integerValue];
            }
            if(i < version2.count){
                value2 = [version2[i] integerValue];
            }
            if(value1  == value2){
                continue;
            }else{
                if(value1 > value2){
                    return NSOrderedDescending;
                }else{
                    return NSOrderedAscending;
                }
            }
        }
        return NSOrderedSame;
    }
    

    测试..

    NSString *version1 = @"3.3.1";
    NSString *version2 = @"3.12.1";
    NSComparisonResult result = [version1 compareVersion:version2];
    switch (result) {
        case NSOrderedAscending:
        case NSOrderedDescending:
        case NSOrderedSame:
             break;
        }
    

    【讨论】:

    • 太棒了!这是在该线程上使用 NSComparisonResult 正确比较 7.28.2 和 7.28 的唯一示例。
    【解决方案5】:

    Sparkle(MacOS 上最流行的软件更新框架)有一个 SUStandardVersionComparator 类来执行此操作,并且还考虑了内部版本号和 beta 标记。 IE。它正确地比较了1.0.5 &gt; 1.0.5b72.0 (2345) &gt; 2.0 (2100)。该代码仅使用 Foundation,因此在 iOS 上也可以正常工作。

    【讨论】:

      【解决方案6】:

      查看我在 github 上实现简单版本检查的 NSString 类别; https://github.com/stijnster/NSString-compareToVersion

      [@"1.2.2.4" compareToVersion:@"1.2.2.5"];
      

      这将返回一个 NSComparisonResult,它比使用更准确;

      [@"1.2.2" compare:@"1.2.2.5" options:NSNumericSearch]
      

      还添加了助手;

      [@"1.2.2.4" isOlderThanVersion:@"1.2.2.5"];
      [@"1.2.2.4" isNewerThanVersion:@"1.2.2.5"];
      [@"1.2.2.4" isEqualToVersion:@"1.2.2.5"];
      [@"1.2.2.4" isEqualOrOlderThanVersion:@"1.2.2.5"];
      [@"1.2.2.4" isEqualOrNewerThanVersion:@"1.2.2.5"];
      

      【讨论】:

        【解决方案7】:

        Swift 2.2 版本:

        let currentStoreAppVersion = "1.10.2"
        let minimumAppVersionRequired = "1.2.2"
        if currentStoreAppVersion.compare(minimumAppVersionRequired, options: NSStringCompareOptions.NumericSearch) ==
                    NSComparisonResult.OrderedDescending {
                    print("Current Store version is higher")
                } else {
                    print("Latest New version is higher")
                }
        

        Swift 3 版本:

        let currentStoreVersion = "1.1.0.2"
        let latestMinimumAppVersionRequired = "1.1.1"
        if currentStoreVersion.compare(latestMinimumAppVersionRequired, options: NSString.CompareOptions.numeric) == ComparisonResult.orderedDescending {
        print("Current version is higher")
        } else {
        print("Latest version is higher")
        }
        

        【讨论】:

          【解决方案8】:

          这里是swift 4.0+版本比较代码

           let currentVersion = "1.2.0"
          
           let oldVersion = "1.1.1"
          
           if currentVersion.compare(oldVersion, options: NSString.CompareOptions.numeric) == ComparisonResult.orderedDescending {
                  print("Higher")
              } else {
                  print("Lower")
              }
          

          【讨论】:

            【解决方案9】:

            我想我只是分享一个为此而汇集的功能。它一点也不完美。请看一下例子和结果。但是,如果您正在检查自己的版本号(我必须这样做来管理数据库迁移之类的事情),那么这可能会有所帮助。

            (当然,删除方法中的日志语句。这些是为了帮助您了解它的全部作用)

            测试:

            [self isVersion:@"1.0" higherThan:@"0.1"];
            [self isVersion:@"1.0" higherThan:@"0.9.5"];
            [self isVersion:@"1.0" higherThan:@"0.9.5.1"];
            [self isVersion:@"1.0.1" higherThan:@"1.0"];
            [self isVersion:@"1.0.0" higherThan:@"1.0.1"];
            [self isVersion:@"1.0.0" higherThan:@"1.0.0"];
            
            // alpha tests
            [self isVersion:@"1.0b" higherThan:@"1.0a"];
            [self isVersion:@"1.0a" higherThan:@"1.0b"];
            [self isVersion:@"1.0a" higherThan:@"1.0a"];
            [self isVersion:@"1.0" higherThan:@"1.0RC1"];
            [self isVersion:@"1.0.1" higherThan:@"1.0RC1"];
            

            结果:

            1.0 > 0.1
            1.0 > 0.9.5
            1.0 > 0.9.5.1
            1.0.1 > 1.0
            1.0.0 < 1.0.1
            1.0.0 == 1.0.0
            1.0b > 1.0a
            1.0a < 1.0b
            1.0a == 1.0a
            1.0 < 1.0RC1       <-- FAILURE
            1.0.1 < 1.0RC1     <-- FAILURE
            

            请注意 alpha 有效,但您必须非常小心。一旦你在某个时候进入 alpha,你就不能通过改变它后面的任何其他次要数字来扩展它。

            代码:

            - (BOOL) isVersion:(NSString *)thisVersionString higherThan:(NSString *)thatVersionString {
            
            // LOWER
            if ([thisVersionString compare:thatVersionString options:NSNumericSearch] == NSOrderedAscending) {
                NSLog(@"%@ < %@", thisVersionString, thatVersionString);
                return NO;
            }
            
            // EQUAL
            if ([thisVersionString compare:thatVersionString options:NSNumericSearch] == NSOrderedSame) {
                NSLog(@"%@ == %@", thisVersionString, thatVersionString);
                return NO;
            }
            
            NSLog(@"%@ > %@", thisVersionString, thatVersionString);
            // HIGHER
            return YES;
            }
            

            【讨论】:

              【解决方案10】:

              我的 iOS 库 AppUpdateTracker 包含一个 NSString category 来执行这种比较。 (实现基于DonnaLea's answer。)

              用法如下:

              [@"1.4" isGreaterThanVersionString:@"1.3"]; // YES
              [@"1.4" isLessThanOrEqualToVersionString:@"1.3"]; // NO
              

              此外,您可以使用它来跟踪应用的安装/更新状态:

              [AppUpdateTracker registerForAppUpdatesWithBlock:^(NSString *previousVersion, NSString *currentVersion) {
                  NSLog(@"app updated from: %@ to: %@", previousVersion, currentVersion);
              }];
              [AppUpdateTracker registerForFirstInstallWithBlock:^(NSTimeInterval installTimeSinceEpoch, NSUInteger installCount) {
                  NSLog(@"first install detected at: %f amount of times app was (re)installed: %lu", installTimeSinceEpoch, (unsigned long)installCount);
              }];
              [AppUpdateTracker registerForIncrementedUseCountWithBlock:^(NSUInteger useCount) {
                  NSLog(@"incremented use count to: %lu", (unsigned long)useCount);
              }];
              

              【讨论】:

              • 是 v4.21
              • 不,4.21 被认为大于 4.3,因为 21 > 3。为了满足您的相等比较,您需要将 4.21 与 4.30 进行比较。请看Nathan de Vries's answer的cmets中的讨论。
              【解决方案11】:

              Glibc 有一个函数strverscmpversionsort……不幸的是,它不能移植到 iPhone,但你可以很容易地编写自己的函数。这种(未经测试的)重新实现仅来自阅读记录的行为,而不是来自阅读 Glibc 的源代码。

              int strverscmp(const char *s1, const char *s2) {
                  const char *b1 = s1, *b2 = s2, *e1, *e2;
                  long n1, n2;
                  size_t z1, z2;
                  while (*b1 && *b1 == *b2) b1++, b2++;
                  if (!*b1 && !*b2) return 0;
                  e1 = b1, e2 = b2;
                  while (b1 > s1 && isdigit(b1[-1])) b1--;
                  while (b2 > s2 && isdigit(b2[-1])) b2--;
                  n1 = strtol(b1, &e1, 10);
                  n2 = strtol(b2, &e2, 10);
                  if (b1 == e1 || b2 == e2) return strcmp(s1, s2);
                  if (n1 < n2) return -1;
                  if (n1 > n2) return 1;
                  z1 = strspn(b1, "0"), z2 = strspn(b2, "0");
                  if (z1 > z2) return -1;
                  if (z1 < z2) return 1;
                  return 0;
              }
              

              【讨论】:

              • 这看起来太可怕了。我最喜欢 Objective-C 的一件事是,在大多数情况下,我不再需要处理普通的 C。
              【解决方案12】:

              如果您知道每个版本号正好有 3 个用点分隔的整数,您可以解析它们(例如使用 sscanf(3))并比较它们:

              const char *version1str = "1.0.1";
              const char *version2str = "1.2.5";
              int major1, minor1, patch1;
              int major2, minor2, patch2;
              if(sscanf(version1str, "%d.%d.%d", &major1, &minor1, &patch1) == 3 &&
                 sscanf(version2str, "%d.%d.%d", &major2, &minor2, &patch2) == 3)
              {
                  // Parsing succeeded, now compare the integers
                  if(major1 > major2 ||
                    (major1 == major2 && (minor1 > minor2 ||
                                         (minor1 == minor2 && patch1 > patch2))))
                  {
                      // version1 > version2
                  }
                  else if(major1 == major2 && minor1 == minor2 && patch1 == patch2)
                  {
                      // version1 == version2
                  }
                  else
                  {
                      // version1 < version2
                  }
              }
              else
              {
                  // Handle error, parsing failed
              }
              

              【讨论】:

                【解决方案13】:

                要快速检查版本,您可以使用以下方法

                switch newVersion.compare(currentversion, options: NSStringCompareOptions.NumericSearch) {
                    case .OrderedDescending:
                        println("NewVersion available  ")
                        // Show Alert Here
                
                    case .OrderedAscending:
                        println("NewVersion Not available  ")
                    default:
                        println("default")
                    }
                

                希望对您有所帮助。

                【讨论】:

                  【解决方案14】:

                  这是一个递归函数,可以处理任意长度的多个版本格式。它也适用于@"1.0" 和@"1.0.0"

                  static inline NSComparisonResult versioncmp(const NSString * a, const NSString * b)
                  {
                      if ([a isEqualToString:@""] && [b isEqualToString:@""]) {
                          return NSOrderedSame;
                      }
                  
                      if ([a isEqualToString:@""]) {
                          a = @"0";
                      }
                  
                      if ([b isEqualToString:@""]) {
                          b = @"0";
                      }
                  
                      NSArray<NSString*> * aComponents = [a componentsSeparatedByString:@"."];
                      NSArray<NSString*> * bComponents = [b componentsSeparatedByString:@"."];
                      NSComparisonResult r = [aComponents[0] compare:bComponents[0] options:NSNumericSearch];
                  
                      if(r != NSOrderedSame) {
                          return r;
                      } else {
                          NSString* newA = (a.length == aComponents[0].length) ? @"" : [a substringFromIndex:aComponents[0].length+1];
                          NSString* newB = (b.length == bComponents[0].length) ? @"" : [b substringFromIndex:bComponents[0].length+1];
                          return versioncmp(newA, newB);
                      }
                  
                  }
                  

                  测试样本:

                  versioncmp(@"11.5", @"8.2.3");
                  versioncmp(@"1.5", @"8.2.3");
                  versioncmp(@"1.0", @"1.0.0");
                  versioncmp(@"11.5.3.4.1.2", @"11.5.3.4.1.2");
                  

                  【讨论】:

                    【解决方案15】:

                    基于@nathan-de-vries 的answer,我写了SemanticVersion.swift 用于比较Semantic Version

                    特点:

                    1.21.2.0-alpha1.2.0-beta1.2.01.2.0+build1.2.0.01.2.991.11.0

                    代码:

                    public struct SemanticVersion: ExpressibleByStringLiteral, ExpressibleByIntegerLiteral, ExpressibleByFloatLiteral {
                        public var stringLiteral: String
                        public init(stringLiteral literal: String) {
                            stringLiteral = literal
                        }
                        public init(integerLiteral literal: Int) {
                            stringLiteral = String(literal)
                        }
                        public init(floatLiteral literal: Double) {
                            stringLiteral = String(literal)
                        }
                    }
                    
                    extension SemanticVersion: Comparable {
                        private var prepared: String {
                            return stringLiteral.replacingOccurrences(of: "-", with: "\0") + "\n"
                        }
                        public static func < (a: SemanticVersion, b: SemanticVersion) -> Bool {
                            return a.prepared.compare(b.prepared, options: .numeric) == .orderedAscending
                        }
                    }
                    
                    // MARK: EXTENSION
                    
                    public extension String {
                        var semanticVersion: SemanticVersion {
                            return SemanticVersion(stringLiteral: self)
                        }
                    }
                    
                    public extension Int {
                        var semanticVersion: SemanticVersion {
                            return SemanticVersion(integerLiteral: self)
                        }
                    }
                    
                    public extension Double {
                        var semanticVersion: SemanticVersion {
                            return SemanticVersion(floatLiteral: self)
                        }
                    }
                    

                    用法/测试用例:

                    XCTAssertTrue(UIDevice.current.systemVersion.semanticVersion >= 14.0)
                    
                    XCTAssertTrue(2.semanticVersion         < "2.0-a")
                    XCTAssertTrue("2.0-a".semanticVersion   < "2.0-b")
                    XCTAssertTrue("2.0-b".semanticVersion   < 2.0)
                    XCTAssertTrue(2.0.semanticVersion       < "2.0+a")
                    XCTAssertTrue("2.0+a".semanticVersion   < "2.0+b")
                    XCTAssertTrue("2.0+b".semanticVersion   < "2.0.0")
                    XCTAssertTrue("2.0.0".semanticVersion   < 2.1)
                    XCTAssertTrue(2.1.semanticVersion       < 2.99)
                    XCTAssertTrue(2.99.semanticVersion      < 11.0)
                    

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2010-12-15
                      • 1970-01-01
                      • 2022-10-24
                      • 2013-09-06
                      • 2012-09-30
                      • 2023-03-20
                      • 2021-09-18
                      相关资源
                      最近更新 更多