Vergleichen Sie die Versionsnummern in Objective-C


87

Ich schreibe eine Anwendung, die Daten mit Artikeln und Versionsnummern empfängt. Die Zahlen sind wie "1.0.1" oder "1.2.5" formatiert. Wie kann ich diese Versionsnummern vergleichen? Ich denke, sie müssen zuerst als String formatiert werden, oder? Welche Optionen muss ich haben, um festzustellen, dass "1.2.5" nach "1.0.1" kommt?


Ich habe diese kleine Bibliothek geschrieben, um 2 Versionen von Strings in Obj-C einfach zu vergleichen. Normalerweise in iOS. Haben Sie Beispiele und Codes auf der GitHub-Seite
Nembleton

3
Es hilft, genau zu klären, was das Versionsschema ist. Einige haben möglicherweise Formate, die zusätzliche Logik erfordern.
Uchuugaka

Antworten:


242

Dies ist der einfachste Weg, um Versionen zu vergleichen, wobei zu beachten ist, dass "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
Ich habe diese Methode verwendet und kürzlich festgestellt, dass sie beim Vergleich von 2.4.06 mit 2.4.4 falsche Ergebnisse liefert (was ich für falsch halte). Ich glaube, dass 2.4.06 niedriger als 2.4.4 sein sollte, aber vielleicht irre ich mich ... irgendwelche Gedanken?
Omer

7
@Omer: Warum 06 und nicht 6? Ich denke, die meisten Entwickler würden 2.4.06 als eine höhere Version als 2.4.4 betrachten.
Stephen Melvin

4
Dies ist schön und einfach, basiert jedoch auf einem sehr einfachen Versionsschema.
Uchuugaka

10
@ScottBerrevoets Ich würde sicherlich hoffen, dass es nicht so funktioniert, da dies bedeuten würde, dass "1.2.3" kleiner als "1.1.12" ist (123 <1112)! Wie Apple sorgfältig feststellt, werden " Zahlen in Zeichenfolgen mit numerischen Werten verglichen ". Das heißt, Sätze von Zahlen innerhalb von Zeichenfolgen werden jeweils verglichen (im Wesentlichen der componentsSeparatedByStringAnsatz). Sie können dies selbst mit @"1.8"vs testen @"1.7.2.3.55"und sehen, dass 1.8 die Nase vorn hat.
Dooleyo

3
NSNumericSearch glaubt, dass "1.0" kleiner als "1.0.0" ist. Nicht ganz flexibel genug für meine Zwecke.
Bobics

17

Ich werde meine Methode hinzufügen, die streng numerische Versionen (no a, b, RC usw.) mit einer beliebigen Anzahl von Komponenten vergleicht.

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

12

Dies ist eine Erweiterung der Antwort von Nathan de Vries, um das Problem 1 <1.0 <1.0.0 usw. anzugehen.

Zunächst können wir das Problem der zusätzlichen ".0" in unserer Versionszeichenfolge mit einer NSStringKategorie behandeln:

@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

Mit der obigen NSStringKategorie können wir unsere Versionsnummern kürzen, um die unnötigen .0 zu löschen

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

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

Jetzt können wir noch den wunderbar einfachen Ansatz von Nathan de Vries verwenden:

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

Zusammen mit der Lösung von Nathan de Vries ist dies die beste und eleganteste Antwort.
Dalmazio

Würde das nicht immer noch sagen, dass 7.4.2 eine höhere Version als 7.5 ist?
Tres

@Tres Nr. Da NSNumericSearch als Option übergeben wird, werden die Zeichenfolgen als Zahlen verglichen, also 7.4.2 <7.5
DonnaLea

9

Ich habe es selbst gemacht, benutze Kategorie ..

Quelle..

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

Prüfung..

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

Genial! Dies ist das einzige Beispiel für die Verwendung von NSComparisonResult in diesem Thread, in dem 7.28.2 und 7.28 korrekt verglichen werden.
CokePokes

7

Sparkle (das beliebteste Software-Update-Framework für MacOS) verfügt über eine SUStandardVersionComparator- Klasse, die dies tut und auch Build-Nummern und Beta-Marker berücksichtigt. Dh es vergleicht richtig 1.0.5 > 1.0.5b7oder 2.0 (2345) > 2.0 (2100). Der Code verwendet nur Foundation und sollte daher auch unter iOS einwandfrei funktionieren.


6

Schauen Sie sich meine NSString-Kategorie an, die eine einfache Versionsprüfung auf Github implementiert. https://github.com/stijnster/NSString-compareToVersion

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

Dies gibt ein NSComparisonResult zurück, das genauer ist als die Verwendung von;

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

Helfer werden ebenfalls hinzugefügt.

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

Swift 2.2 Version:

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 Version:

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

3

Ich dachte, ich würde nur eine Funktion teilen, die ich dafür zusammengestellt habe. Es ist überhaupt nicht perfekt. Bitte schauen Sie sich die Beispiele und Ergebnisse an. Aber wenn Sie Ihre eigenen Versionsnummern überprüfen (was ich tun muss, um Dinge wie Datenbankmigrationen zu verwalten), kann dies ein wenig helfen.

(Entfernen Sie natürlich auch die Protokollanweisungen in der Methode. Diese dienen dazu, Ihnen zu zeigen, was alles bewirkt.)

Tests:

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

Ergebnisse:

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

Beachten Sie, dass Alpha funktioniert, aber Sie müssen sehr vorsichtig damit sein. Sobald Sie irgendwann Alpha werden, können Sie dies nicht mehr erweitern, indem Sie andere kleinere Zahlen dahinter ändern.

Code:

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

Meine iOS-Bibliothek AppUpdateTracker enthält eine NSString-Kategorie , um diese Art von Vergleich durchzuführen. (Die Implementierung basiert auf der Antwort von DonnaLea .)

Die Verwendung wäre wie folgt:

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

Darüber hinaus können Sie damit den Installations- / Aktualisierungsstatus Ihrer App verfolgen:

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

ist v4.21 <4.3? if ([thisVersion isGreaterThanOrEqualToVersionString: @ "4.3"])
johndpope

Nein, 4,21 wird als größer als 4,3 als 21> 3 angesehen. Um Ihren Gleichheitsvergleich zu erfüllen, möchten Sie 4,21 mit 4,30 vergleichen. Bitte beachten Sie die Diskussion in den Kommentaren der Antwort von Nathan de Vries .
Stunner

3

Hier ist der schnelle 4.0 + Code für den Versionsvergleich

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

0

Glibc hat eine Funktion strverscmpund versionsort… ist leider nicht auf das iPhone tragbar, aber Sie können Ihre eigene ziemlich einfach schreiben. Diese (nicht getestete) Neuimplementierung erfolgt nur durch Lesen des dokumentierten Verhaltens und nicht durch Lesen des Quellcodes von 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
das sieht einfach schrecklich aus. Eines der Dinge, die ich an Objective-C am meisten mag, ist, dass ich mich größtenteils nicht mehr mit einfachem C beschäftigen muss.
Lukas Petr

0

Wenn Sie wissen, dass jede Versionsnummer genau 3 durch Punkte getrennte Ganzzahlen enthält, können Sie sie analysieren (z. B. mit sscanf(3)) und vergleichen:

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

Um die Version schnell zu überprüfen, können Sie Folgendes verwenden

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

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

Hoffe es könnte hilfreich sein.


0

Hier ist eine rekursive Funktion, die mit mehreren Versionsformatierungen beliebiger Länge arbeitet. Es funktioniert auch für @ "1.0" und @ "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);
    }

}

Testmuster:

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");
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.