Estoy escribiendo una aplicación que recibe datos con elementos y números de versión. Los números tienen el formato "1.0.1" o "1.2.5". ¿Cómo puedo comparar estos números de versión? Creo que primero deben formatearse como una cadena, ¿no? ¿Qué opciones tengo para determinar que "1.2.5" viene después de "1.0.1"?
objective-c
comparison
mlecho
fuente
fuente
Respuestas:
Esta es la forma más sencilla de comparar versiones, teniendo en cuenta que "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 }
fuente
componentsSeparatedByString
enfoque). Puede probar esto usted mismo con@"1.8"
vs@"1.7.2.3.55"
y ver que 1.8 sale adelante.Agregaré mi método, que compara versiones estrictamente numéricas (no a, b, RC, etc.) con cualquier número de componentes.
+ (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; }
fuente
Esta es una expansión de la respuesta de Nathan de Vries para abordar el problema de 1 <1.0 <1.0.0, etc.
En primer lugar, podemos abordar el problema de los ".0" adicionales en nuestra cadena de versión con una
NSString
categoría:@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
Con la
NSString
categoría anterior , podemos acortar nuestros números de versión para eliminar los .0 innecesariosNSString* requiredVersion = @"1.2.0"; NSString* actualVersion = @"1.1.5"; requiredVersion = [requiredVersion shortenedVersionNumberString]; // now 1.2 actualVersion = [actualVersion shortenedVersionNumberString]; // still 1.1.5
Ahora todavía podemos usar el enfoque maravillosamente simple propuesto por Nathan de Vries:
if ([requiredVersion compare:actualVersion options:NSNumericSearch] == NSOrderedDescending) { // actualVersion is lower than the requiredVersion }
fuente
Lo hice yo mismo, uso Categoría ...
Fuente..
@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; }
Prueba..
NSString *version1 = @"3.3.1"; NSString *version2 = @"3.12.1"; NSComparisonResult result = [version1 compareVersion:version2]; switch (result) { case NSOrderedAscending: case NSOrderedDescending: case NSOrderedSame: break; }
fuente
Sparkle (el marco de actualización de software más popular para MacOS) tiene una clase SUStandardVersionComparator que hace esto y también tiene en cuenta los números de compilación y los marcadores beta. Es decir, compara correctamente
1.0.5 > 1.0.5b7
o2.0 (2345) > 2.0 (2100)
. El código solo usa Foundation, por lo que también debería funcionar bien en iOS.fuente
Echa un vistazo a mi categoría NSString que implementa la verificación de versión fácil en github; https://github.com/stijnster/NSString-compareToVersion
[@"1.2.2.4" compareToVersion:@"1.2.2.5"];
Esto devolverá un NSComparisonResult que es más preciso que usar;
[@"1.2.2" compare:@"1.2.2.5" options:NSNumericSearch]
También se agregan ayudantes;
[@"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"];
fuente
Versió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") }
Versió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") }
fuente
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") }
fuente
Pensé en compartir una función que reuní para esto. No es perfecto en absoluto. Eche un vistazo a los ejemplos y resultados. Pero si está verificando sus propios números de versión (lo que tengo que hacer para administrar cosas como las migraciones de bases de datos), esto puede ayudar un poco.
(también, elimine las declaraciones de registro en el método, por supuesto. Están ahí para ayudarlo a ver lo que hace, es todo)
Pruebas:
[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"];
Resultados:
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
note que alpha funciona pero hay que tener mucho cuidado con él. una vez que pasa a alfa en algún momento, no puede extender eso cambiando cualquier otro número menor detrás de él.
Código:
- (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; }
fuente
Mi biblioteca de iOS AppUpdateTracker contiene una categoría NSString para realizar este tipo de comparación. (La implementación se basa en la respuesta de DonnaLea ).
El uso sería el siguiente:
[@"1.4" isGreaterThanVersionString:@"1.3"]; // YES [@"1.4" isLessThanOrEqualToVersionString:@"1.3"]; // NO
Además, puede usarlo para realizar un seguimiento del estado de instalación / actualización de su aplicación:
[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); }];
fuente
Glibc tiene una función
strverscmp
yversionsort
... desafortunadamente, no es portátil para el iPhone, pero puedes escribir la tuya con bastante facilidad. Esta reimplementación (no probada) proviene simplemente de leer el comportamiento documentado, y no de leer el código fuente de 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; }
fuente
Si sabe que cada número de versión tendrá exactamente 3 enteros separados por puntos, puede analizarlos (por ejemplo, usarlos
sscanf(3)
) y compararlos: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 }
fuente
Para verificar la versión en rápido, puede usar lo siguiente
switch newVersion.compare(currentversion, options: NSStringCompareOptions.NumericSearch) { case .OrderedDescending: println("NewVersion available ") // Show Alert Here case .OrderedAscending: println("NewVersion Not available ") default: println("default") }
Espero que te sea de ayuda.
fuente
Aquí hay una función recursiva que hace el trabajo con formato de múltiples versiones de cualquier longitud. También funciona para @ "1.0" y @ "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); } }
Muestras de prueba :
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");
fuente