Objective-C'deki sürüm numaralarını karşılaştırın


87

Öğeler ve sürüm numaraları ile veri alan bir uygulama yazıyorum. Sayılar "1.0.1" veya "1.2.5" şeklinde biçimlendirilmiştir. Bu sürüm numaralarını nasıl karşılaştırabilirim? Sanırım önce bir dize olarak biçimlendirilmeleri gerekiyor, değil mi? "1.2.5" in "1.0.1" den sonra geldiğini belirlemek için hangi seçeneklere ihtiyacım var?


Bu küçük kütüphaneyi Obj-C'de 2 versiyon Strings'i kolayca karşılaştırmak için yazdım. Genellikle iOS'ta. GitHub sayfasında
bulun

3
Versiyonlama şemasının tam olarak ne olduğunu netleştirmeye yardımcı olur. Bazılarının ek mantık gerektiren formatları olabilir.
uchuugaka

Yanıtlar:


244

"1" <"1.0" <"1.0.0" olduğunu göz önünde bulundurarak sürümleri karşılaştırmanın en basit yolu budur:

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

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

6
Bu yöntemi kullanıyordum ve son zamanlarda örneğin 2.4.06 ile 2.4.4'ü karşılaştırırken yanlış sonuçlar verdiğini (düşündüğüm şeyi) buldum. 2.4.06'nın 2.4.4'ten düşük olması gerektiğine inanıyorum, ama belki yanılıyorum ... herhangi bir fikrin var mı?
Omer

7
@Omer: Neden 06 ve 6 değil? Çoğu geliştiricinin 2.4.06'yı 2.4.4'ten daha yüksek bir sürüm olarak değerlendireceğini düşünüyorum.
Stephen Melvin

4
Bu güzel ve basittir, ancak çok basit bir sürüm şemasına dayanır.
uchuugaka

11
@ScottBerrevoets Kesinlikle böyle çalışmadığını umuyorum, çünkü "1.2.3", "1.1.12" den küçük (123 <1112)! Elma dikkatle belirttiği gibi " Sayılar içinde dizeleri sayısal değeri kullanılarak karşılaştırılır ". Yani, dizelerdeki sayı kümelerinin her biri karşılaştırılacaktır (esasen componentsSeparatedByStringyaklaşım). Bunu kendiniz @"1.8"vs ile test edebilir @"1.7.2.3.55"ve 1.8'in öne çıktığını görebilirsiniz.
dooleyo

3
NSNumericSearch, "1.0" ın "1.0.0" dan küçük olduğunu düşünüyor. Benim amaçlarım için yeterince esnek değil.
bobics

17

Kesin olarak sayısal sürümleri (a, b, RC vb. Yok) herhangi bir sayıda bileşenle karşılaştıran yöntemimi ekleyeceğim.

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

Bu, 1 <1.0 <1.0.0 vb. Sorunu çözmek için Nathan de Vries yanıtına yapılan bir genişletmedir.

Öncelikle, sürüm dizimizdeki fazladan ".0" sorununu bir NSStringkategori ile çözebiliriz :

@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

Yukarıdaki NSStringkategoriyle, gereksiz .0'leri düşürmek için sürüm numaralarımızı kısaltabiliriz.

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

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

Şimdi hala Nathan de Vries tarafından önerilen güzel ve basit yaklaşımı kullanabiliriz:

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

Nathan de Vries çözümü ile birleştiğinde bu, en iyi ve en zarif cevaptır.
Dalmazio

Bu hala 7.4.2'nin 7.5'ten daha yüksek bir sürüm olduğunu söylemez mi?
Tres

@Tres hayır. NSNumericSearch seçenek olarak geçtiğinden, dizeler sayı olarak karşılaştırılır, bu nedenle 7.4.2 <7.5
DonnaLea

9

Kendim yaptım, Kategoriyi kullan ..

Kaynak..

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

Ölçek..

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

Harika! Bu, 7.28.2 ve 7.28'i doğru şekilde karşılaştıran NSComparisonResult kullanan tek örnektir.
CokePokes

8

Sparkle (MacOS için en popüler yazılım güncelleme çerçevesi), bunu yapan bir SUStandardVersionComparator sınıfına sahiptir ve ayrıca yapı sayılarını ve beta işaretleyicilerini de hesaba katar. Yani doğru şekilde karşılaştırır 1.0.5 > 1.0.5b7veya 2.0 (2345) > 2.0 (2100). Kod yalnızca Foundation'ı kullanır, bu nedenle iOS'ta da iyi çalışmalıdır.


6

Github'da kolay sürüm denetimi uygulayan NSString kategorime bakın; https://github.com/stijnster/NSString-compareToVersion

Bu, kullanmaktan daha doğru olan bir NSComparisonResult döndürür ;

Yardımcıları da eklendi;


4

Swift 2.2 Sürümü:

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 Sürümü:

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

İşte sürüm karşılaştırması için hızlı 4.0 + kodu

 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

Bunun için bir araya getirdiğim bir işlevi paylaşacağımı düşündüm. Hiç de mükemmel değil. Lütfen örneklere ve sonuçlara bir göz atın. Ancak kendi sürüm numaralarınızı kontrol ediyorsanız (ki bunu veritabanı geçişleri gibi şeyleri yönetmek için yapmam gerekiyor) o zaman bu biraz yardımcı olabilir.

(ayrıca, yöntemdeki günlük ifadelerini de kaldırın, tabii ki. Bunların hepsi ne yaptığını görmenize yardımcı olmak için oradalar)

Testler:

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

Sonuçlar:

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'nın çalıştığını fark et ama çok dikkatli olmalısın. Bir noktada alfaya gittiğinizde, arkasındaki diğer küçük sayıları değiştirerek bunu uzatamazsınız.

Kod:

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

İOS kitaplığım AppUpdateTracker , bu tür bir karşılaştırmayı gerçekleştirmek için bir NSString kategorisi içeriyor . (Uygulama DonnaLea'nın cevabına dayanmaktadır .)

Kullanım aşağıdaki gibi olacaktır:

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

Ek olarak, uygulamanızın kurulum / güncelleme durumunu takip etmek için kullanabilirsiniz:

[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.3 nedir? eğer ([thisVersion isGreaterThanOrEqualToVersionString: @ "4.3"])
johndpope

Hayır, 4,21, 4,21'den büyük 21> 3 olarak kabul edilir. Eşitlik karşılaştırmanızı tatmin etmek için 4,21 ile 4,30'u karşılaştırmak istersiniz. Lütfen Nathan de Vries'in cevabının yorumlarındaki tartışmaya bakın .
Stunner

0

Glibc'in bir işlevi var strverscmpve versionsort… maalesef iPhone'a taşınabilir değil, ancak kendinizinkini oldukça kolay bir şekilde yazabilirsiniz. Bu (test edilmemiş) yeniden uygulama, Glibc'nin kaynak kodunu okumaktan değil, yalnızca belgelenen davranışı okumaktan gelir.

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
bu çok korkunç görünüyor. Objective-C ile ilgili en sevdiğim şeylerden biri, artık basit C ile uğraşmak zorunda kalmamam.
Lukas Petr

0

Her sürüm numarasının noktalarla ayrılmış tam olarak 3 tam sayıya sahip olacağını biliyorsanız, bunları ayrıştırabilirsiniz (örn. sscanf(3) ) ve karşılaştırabilirsiniz:

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

Sürümü hızlı bir şekilde kontrol etmek için aşağıdakileri kullanabilirsiniz

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

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

Umarım yardımcı olur.


0

Burada, herhangi bir uzunlukta birden çok sürüm biçimlendirmesiyle işleri yapan özyinelemeli bir işlev var. Ayrıca @ "1.0" ve @ "1.0.0" için de çalışır

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

}

Test örnekleri :

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");
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.