So sánh số phiên bản trong Objective-C


87

Tôi đang viết một ứng dụng nhận dữ liệu với các mục và số phiên bản. Các số được định dạng như "1.0.1" hoặc "1.2.5". Làm cách nào để so sánh các số phiên bản này? Tôi nghĩ chúng phải được định dạng dưới dạng chuỗi trước, phải không? Tôi có những lựa chọn nào để xác định rằng "1.2.5" đến sau "1.0.1"?


Tôi đã viết thư viện nhỏ đó để dễ dàng so sánh 2 phiên bản Chuỗi trong Obj-C. Điển hình là trong iOS. Có các ví dụ và mã trên trang GitHub
nembleton

3
Nó giúp làm rõ chính xác lược đồ lập phiên bản là gì. Một số có thể có các định dạng yêu cầu logic bổ sung.
uchuugaka

Câu trả lời:


244

Đây là cách đơn giản nhất để so sánh các phiên bản, hãy nhớ rằng "1" <"1.0" <"1.0.0":

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

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

6
Tôi đang sử dụng phương pháp này và gần đây tôi thấy rằng nó trả về (những gì tôi cho là) ​​kết quả sai khi so sánh, ví dụ: 2.4.06 với 2.4.4. Tôi tin rằng 2.4.06 nên thấp hơn 2.4.4, nhưng có lẽ tôi đã sai ... bất kỳ suy nghĩ nào?
Omer

7
@Omer: Tại sao lại là 06 mà không phải là 6? Tôi nghĩ rằng hầu hết các nhà phát triển sẽ coi 2.4.06 là một phiên bản cao hơn 2.4.4.
Stephen Melvin

4
Điều này rất hay và đơn giản nhưng dựa trên một sơ đồ phiên bản rất đơn giản.
uchuugaka

11
@ScottBerrevoets Tôi chắc chắn hy vọng đó không phải là cách nó hoạt động, vì điều đó có nghĩa là "1.2.3" nhỏ hơn "1.1.12" (123 <1112)! Như Apple đã tuyên bố cẩn thận, " Các số trong chuỗi được so sánh bằng cách sử dụng giá trị số ". Nghĩa là, các tập hợp số bên trong các chuỗi sẽ được so sánh với nhau (về cơ bản, componentsSeparatedByStringcách tiếp cận). Bạn có thể tự kiểm tra điều này với @"1.8"vs @"1.7.2.3.55"và thấy rằng 1.8 xuất hiện ở phía trước.
dooleyo

3
NSNumericSearch cho rằng "1.0" nhỏ hơn "1.0.0". Không hoàn toàn đủ linh hoạt cho các mục đích của tôi.
bobics

17

Tôi sẽ thêm phương thức của mình, so sánh các phiên bản số hoàn toàn (không có a, b, RC, v.v.) với bất kỳ số thành phần nào.

+ (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;
}

13

Đây là phần mở rộng cho câu trả lời Nathan de Vries để giải quyết vấn đề 1 <1,0 <1,0,0, v.v.

Trước hết, chúng tôi có thể giải quyết vấn đề có thêm ".0" trên chuỗi phiên bản của chúng tôi với một NSStringdanh mục:

@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

Với NSStringdanh mục trên, chúng tôi có thể rút ngắn số phiên bản của mình để loại bỏ .0 không cần thiết

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

requiredVersion = [requiredVersion shortenedVersionNumberString]; // now 1.2
actualVersion = [actualVersion shortenedVersionNumberString]; // still 1.1.5

Bây giờ chúng ta vẫn có thể sử dụng cách tiếp cận đơn giản tuyệt đẹp do Nathan de Vries đề xuất:

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

Điều này cùng với giải pháp Nathan de Vries là câu trả lời tốt nhất và thanh lịch nhất.
Dalmazio

Điều này sẽ không vẫn nói rằng 7.4.2 là một phiên bản cao hơn 7.5?
Tres

@ Không. Vì NSNumericSearch được chuyển vào dưới dạng tùy chọn, các chuỗi được so sánh dưới dạng số, do đó 7.4.2 <7.5
DonnaLea

9

Tôi tự làm, sử dụng Category ..

Nguồn ..

@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;
}

Kiểm tra..

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

Tuyệt vời! Đây là ví dụ duy nhất sử dụng NSComparisonResult trên luồng này để so sánh 7.28.2 & 7.28 một cách chính xác.
CokePokes,

8

Sparkle (khung cập nhật phần mềm phổ biến nhất dành cho MacOS) có lớp SUStandardVersionComparator thực hiện việc này và cũng tính đến số bản dựng và điểm đánh dấu beta. Tức là nó so sánh chính xác 1.0.5 > 1.0.5b7hoặc 2.0 (2345) > 2.0 (2100). Mã chỉ sử dụng Foundation, vì vậy cũng sẽ hoạt động tốt trên iOS.


6

Kiểm tra danh mục NSString của tôi thực hiện kiểm tra phiên bản dễ dàng trên github; https://github.com/stijnster/NSString-compareToVersion

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

Điều này sẽ trả về NSComparisonResult chính xác hơn khi sử dụng;

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

Người trợ giúp cũng được thêm vào;

[@"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"];

4

Phiên bản 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")
        }

Phiên bản 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")
}

4

Đây là mã nhanh chóng 4.0 + để so sánh phiên bản

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

3

Tôi nghĩ tôi chỉ chia sẻ một chức năng mà tôi đã tập hợp lại cho việc này. Nó không hoàn hảo chút nào. Hãy xem các ví dụ và kết quả. Nhưng nếu bạn đang kiểm tra số phiên bản của riêng mình (mà tôi phải làm để quản lý những thứ như di chuyển cơ sở dữ liệu) thì điều này có thể giúp ích một chút.

(tất nhiên, hãy xóa các câu lệnh nhật ký trong phương thức. những câu lệnh đó ở đó để giúp bạn xem tất cả những gì nó làm)

Kiểm tra:

[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"];

Các kết quả:

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

chú ý rằng alpha hoạt động nhưng bạn phải rất cẩn thận với nó. một khi bạn chuyển sang alpha ở một số điểm, bạn không thể mở rộng điều đó bằng cách thay đổi bất kỳ số nhỏ nào khác đằng sau nó.

Mã:

- (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;
}

3

Thư viện iOS AppUpdateTracker của tôi chứa danh mục NSString để thực hiện loại so sánh này. (Việc thực hiện dựa trên câu trả lời của DonnaLea .)

Cách sử dụng sẽ như sau:

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

Ngoài ra, bạn có thể sử dụng nó để theo dõi trạng thái cài đặt / cập nhật ứng dụng của mình:

[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 có <4.3 không? if ([thisVersion isGreaterThanOrEqualToVersionString: @ "4.3"])
johndpope

Không, 4,21 được coi là lớn hơn 4,3 vì 21> 3. Để đáp ứng so sánh bình đẳng của bạn, bạn muốn so sánh 4,21 với 4,30. Vui lòng xem thảo luận trong phần bình luận câu trả lời của Nathan de Vries .
Stunner

0

Glibc có một chức năng strverscmpversionsort... thật không may, không thể di động vào iPhone, nhưng bạn có thể viết của riêng mình khá dễ dàng. Việc triển khai lại (chưa được kiểm tra) này xuất phát từ việc chỉ đọc hành vi được ghi lại, chứ không phải từ việc đọc mã nguồn của 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;
}

2
điều này trông thật kinh khủng. Một trong những điều mà tôi thích nhất về Objective-C là hầu hết tôi không còn phải đối mặt với C đơn giản nữa.
Lukas Petr

0

Nếu bạn biết mỗi số phiên bản sẽ có chính xác 3 số nguyên được phân tách bằng dấu chấm, bạn có thể phân tích cú pháp chúng (ví dụ: sử dụng sscanf(3)) và so sánh chúng:

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
}

0

Để kiểm tra phiên bản nhanh chóng, bạn có thể sử dụng sau

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

    case .OrderedAscending:
        println("NewVersion Not available  ")
    default:
        println("default")
    }

Hy vọng nó có thể hữu ích.


0

Đây là một hàm đệ quy hoạt động với nhiều định dạng phiên bản có độ dài bất kỳ. Nó cũng hoạt động cho @ "1.0" và @ "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);
    }

}

Mẫu thử nghiệm :

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");
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.