Objective-C에서 버전 번호 비교


87

항목과 버전 번호가있는 데이터를받는 응용 프로그램을 작성 중입니다. 숫자는 "1.0.1"또는 "1.2.5"와 같은 형식입니다. 이 버전 번호를 어떻게 비교할 수 있습니까? 먼저 문자열로 형식을 지정해야한다고 생각합니다. "1.2.5"가 "1.0.1"뒤에 오는지 확인하려면 어떤 옵션이 필요합니까?


Obj-C에서 두 가지 버전의 문자열을 쉽게 비교하기 위해 작은 라이브러리를 작성했습니다. 일반적으로 iOS에서. 온 예제와 코드를 가지고 GitHub의 페이지
nembleton

3
버전 관리 체계가 무엇인지 정확하게 설명하는 데 도움이됩니다. 일부는 추가 논리가 필요한 형식을 가질 수 있습니다.
uchuugaka

답변:


244

"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
이 방법을 사용하고 있었는데 최근 2.4.06을 2.4.4와 비교할 때 잘못된 결과를 반환한다는 것을 발견했습니다. 2.4.06이 2.4.4보다 낮아야한다고 생각 합니다만, 제가 틀렸을 수도 있습니다.
Omer

7
@Omer : 왜 06이 아니라 6일까요? 대부분의 개발자는 2.4.06을 2.4.4보다 높은 버전으로 간주 할 것입니다.
Stephen Melvin 2012

4
이것은 훌륭하고 간단하지만 매우 간단한 버전 체계에 의존합니다.
uchuugaka

11
@ScottBerrevoets "1.2.3"이 "1.1.12"(123 <1112)보다 작다는 것을 의미하므로 이것이 작동하는 방식이 아니기를 확실히 바랄 것입니다! 애플이 조심스럽게 상태로 " 숫자 에서 문자열은 숫자 값을 사용하여 비교 ". 즉, 문자열 내의 숫자 집합 이 각각 비교됩니다 (본질적으로 componentsSeparatedByString접근 방식). @"1.8"vs를 사용 @"1.7.2.3.55"하여 직접 테스트 하고 1.8이 앞서 나오는 것을 볼 수 있습니다 .
dooleyo

3
NSNumericSearch는 "1.0"이 "1.0.0"보다 작다고 생각합니다. 내 목적에 충분히 유연하지 않습니다.
bobics 2014 년

17

엄격하게 숫자 버전 (a, b, RC 등 없음)을 여러 구성 요소와 비교하는 방법을 추가하겠습니다.

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

이것은 1 <1.0 <1.0.0 등의 문제를 해결하기 위해 Nathan de Vries 답변의 확장입니다.

먼저 NSString카테고리를 사용하여 버전 문자열에서 추가 ".0"문제를 해결할 수 있습니다 .

@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

위의 NSString카테고리를 사용하면 불필요한 .0을 삭제하기 위해 버전 번호를 줄일 수 있습니다.

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

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

이제 우리는 Nathan de Vries가 제안한 아름답고 간단한 접근 방식을 사용할 수 있습니다.

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

이것은 Nathan de Vries 솔루션과 결합되어 가장 좋고 가장 우아한 대답입니다.
Dalmazio 2014 년

7.4.2가 7.5보다 높은 버전이라고 여전히 말하지 않습니까?
Tres

@Tres 아니. NSNumericSearch가 옵션으로 전달되기 때문에 문자열이 숫자로 비교되므로 7.4.2 <7.5
DonnaLea

9

직접 만들었습니다. 카테고리 사용 ..

출처..

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

테스트..

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

대박! 이것은 7.28.2와 7.28을 올바르게 비교하는이 스레드에서 NSComparisonResult를 사용하는 유일한 예입니다.
CokePokes

8

Sparkle (MacOS에서 가장 널리 사용되는 소프트웨어 업데이트 프레임 워크)에는이를 수행하는 SUStandardVersionComparator 클래스 가 있으며 빌드 번호와 베타 마커도 고려합니다. 즉, 1.0.5 > 1.0.5b7또는 2.0 (2345) > 2.0 (2100). 코드는 Foundation 만 사용하므로 iOS에서도 잘 작동합니다.


6

github에서 쉬운 버전 검사를 구현하는 내 NSString 카테고리를 확인하십시오. https://github.com/stijnster/NSString-compareToVersion

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

그러면 사용하는 것보다 더 정확한 NSComparisonResult 가 반환 됩니다.

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

도우미도 추가됩니다.

[@"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 버전 :

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 버전 :

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

다음은 버전 비교를위한 신속한 4.0 + 코드입니다.

 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

나는 이것을 위해 내가 모은 기능을 공유 할 것이라고 생각했습니다. 전혀 완벽하지 않습니다. 그 예와 결과를보세요. 그러나 자체 버전 번호를 확인하는 경우 (데이터베이스 마이그레이션과 같은 작업을 관리하기 위해 수행해야 함) 약간 도움이 될 수 있습니다.

(물론 메서드에서 로그 문을 제거하십시오. 그것이하는 일을 확인하는 데 도움이되는 것이 전부입니다)

테스트 :

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

결과 :

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

알파는 작동하지만 매우 조심해야합니다. 어떤 시점에서 알파가되면 그 뒤에있는 다른 마이너 번호를 변경하여 확장 할 수 없습니다.

암호:

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

내 iOS 라이브러리 AppUpdateTracker 에는 이러한 종류의 비교를 수행 하는 NSString 범주 가 포함되어 있습니다 . (구현은 DonnaLea의 답변을 기반으로합니다 .)

사용법은 다음과 같습니다.

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

또한 앱의 설치 / 업데이트 상태를 추적하는 데 사용할 수 있습니다.

[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입니까? if ([thisVersion isGreaterThanOrEqualToVersionString : @ "4.3"])
johndpope

아니요, 4.21은 21> 3으로 4.3보다 큰 것으로 간주됩니다. 동등성 비교를 충족하려면 4.21과 4.30을 비교해야합니다. Nathan de Vries의 답변 에 대한 의견에서 토론을 참조하십시오 .
Stunner

0

Glibc에는 기능이 strverscmp있고 versionsort… 안타깝게도 iPhone에 이식 할 수는 없지만 사용자는 쉽게 작성할 수 있습니다. 이 (예상되지 않은) 재 구현은 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
끔찍해 보입니다. Objective-C에 대해 내가 가장 좋아하는 것 중 하나는 대부분의 경우 더 이상 일반 C를 다룰 필요가 없다는 것입니다.
Lukas Petr

0

각 버전 번호에 점으로 구분 된 정확히 3 개의 정수가 있다는 것을 알고 있다면이를 구문 분석 (예 : 사용 sscanf(3))하고 비교할 수 있습니다.

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

신속하게 버전을 확인하려면 다음을 사용할 수 있습니다.

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

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

도움이 되었기를 바랍니다.


0

다음은 길이에 관계없이 여러 버전 형식으로 작업을 수행하는 재귀 함수입니다. @ "1.0"및 @ "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);
    }

}

테스트 샘플 :

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");
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.