Piszę aplikację, która otrzymuje dane z pozycjami i numerami wersji. Liczby mają format „1.0.1” lub „1.2.5”. Jak mogę porównać te numery wersji? Myślę, że najpierw należy je sformatować jako ciąg, nie? Jakie opcje są potrzebne, aby określić, że wersja „1.2.5” pojawi się po wersji „1.0.1”?
objective-c
comparison
mlecho
źródło
źródło
Odpowiedzi:
To najprostszy sposób porównywania wersji, pamiętając, że „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 }
źródło
componentsSeparatedByString
podejście). Możesz to sprawdzić samemu w@"1.8"
vs@"1.7.2.3.55"
i przekonać się, że 1.8 wychodzi na prowadzenie.Dodam moją metodę, która porównuje wersje ściśle numeryczne (bez a, b, RC itp.) Z dowolną liczbą komponentów.
+ (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; }
źródło
To jest rozszerzenie do odpowiedzi Nathana de Vriesa w celu rozwiązania problemu 1 <1,0 <1,0,0 itd.
Po pierwsze, możemy rozwiązać problem z dodatkowymi „.0” w ciągu naszej wersji z
NSString
kategorią:@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
Dzięki powyższej
NSString
kategorii możemy skrócić numery naszych wersji, aby usunąć niepotrzebne .0NSString* requiredVersion = @"1.2.0"; NSString* actualVersion = @"1.1.5"; requiredVersion = [requiredVersion shortenedVersionNumberString]; // now 1.2 actualVersion = [actualVersion shortenedVersionNumberString]; // still 1.1.5
Teraz możemy nadal stosować pięknie proste podejście zaproponowane przez Nathana de Vries:
if ([requiredVersion compare:actualVersion options:NSNumericSearch] == NSOrderedDescending) { // actualVersion is lower than the requiredVersion }
źródło
Zrobiłem to sam, użyj kategorii.
Źródło..
@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; }
Test..
NSString *version1 = @"3.3.1"; NSString *version2 = @"3.12.1"; NSComparisonResult result = [version1 compareVersion:version2]; switch (result) { case NSOrderedAscending: case NSOrderedDescending: case NSOrderedSame: break; }
źródło
Sparkle (najpopularniejsza platforma aktualizacji oprogramowania dla systemu MacOS) ma klasę SUStandardVersionComparator , która to robi, a także bierze pod uwagę numery kompilacji i znaczniki beta. To znaczy poprawnie porównuje
1.0.5 > 1.0.5b7
lub2.0 (2345) > 2.0 (2100)
. Kod używa tylko Foundation, więc powinien działać dobrze również na iOS.źródło
Sprawdź moją kategorię NSString, która implementuje łatwe sprawdzanie wersji na github; https://github.com/stijnster/NSString-compareToVersion
[@"1.2.2.4" compareToVersion:@"1.2.2.5"];
To zwróci wynik NSComparisonResult, który jest dokładniejszy niż użycie;
[@"1.2.2" compare:@"1.2.2.5" options:NSNumericSearch]
Dodaje się również pomocników;
[@"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"];
źródło
Wersja 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") }
Wersja 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") }
źródło
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") }
źródło
Pomyślałem, że po prostu podzielę się funkcją, którą wykorzystałem w tym celu. To wcale nie jest doskonałe. Proszę spojrzeć na przykłady i wyniki. Ale jeśli sprawdzasz własne numery wersji (co muszę zrobić, aby zarządzać takimi rzeczami, jak migracje baz danych), może to trochę pomóc.
(oczywiście usuń również instrukcje dziennika w metodzie. te są po to, aby pomóc ci zobaczyć, co robi)
Testy:
[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"];
Wyniki:
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
Zauważ, że alfa działa, ale musisz być z nią bardzo ostrożny. kiedy w pewnym momencie przejdziesz do alfy, nie możesz jej rozszerzyć, zmieniając inne mniejsze liczby za nią.
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; }
źródło
Moja biblioteka AppUpdateTracker na iOS zawiera kategorię NSString do wykonywania tego rodzaju porównań. (Wdrożenie opiera się na odpowiedzi DonnaLea .)
Sposób użycia byłby następujący:
[@"1.4" isGreaterThanVersionString:@"1.3"]; // YES [@"1.4" isLessThanOrEqualToVersionString:@"1.3"]; // NO
Ponadto możesz go używać do śledzenia stanu instalacji / aktualizacji aplikacji:
[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); }];
źródło
Glibc ma funkcję
strverscmp
iversionsort
… niestety nie jest przenośny na iPhone'a, ale można dość łatwo napisać własne. Ta (nieprzetestowana) ponowna implementacja pochodzi z samego przeczytania udokumentowanego zachowania, a nie z czytania kodu źródłowego 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; }
źródło
Jeśli wiesz, że każdy numer wersji będzie miał dokładnie 3 liczby całkowite oddzielone kropkami, możesz je przeanalizować (np. Używając
sscanf(3)
) i porównać: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 }
źródło
Aby szybko sprawdzić wersję, możesz użyć następującego
switch newVersion.compare(currentversion, options: NSStringCompareOptions.NumericSearch) { case .OrderedDescending: println("NewVersion available ") // Show Alert Here case .OrderedAscending: println("NewVersion Not available ") default: println("default") }
Mam nadzieję, że to może być pomocne.
źródło
Oto funkcja rekurencyjna, która działa z formatowaniem wielu wersji o dowolnej długości. Działa również dla @ „1.0” i @ „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); } }
Próbki do badań:
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");
źródło