Objective-Cのバージョン番号を比較する


87

アイテムとバージョン番号を含むデータを受信するアプリケーションを作成しています。番号は「1.0.1」または「1.2.5」のようにフォーマットされます。これらのバージョン番号を比較するにはどうすればよいですか?最初に文字列としてフォーマットする必要があると思いますね。「1.2.5」が「1.0.1」の後に来ると判断するには、どのようなオプションが必要ですか?


Obj-Cで2つのバージョンの文字列を簡単に比較できるように、この小さなライブラリを作成しました。通常、iOSで。GitHubページ
nembleton 2012年

3
バージョン管理スキームが何であるかを正確に明確にするのに役立ちます。一部には、追加のロジックを必要とするフォーマットがある場合があります。
uchuugaka 2013年

回答:


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 2012年

7
@Omer:なぜ6ではなく06なのですか?ほとんどの開発者は、2.4.06を2.4.4よりも高いバージョンと見なすと思います。
スティーブンメルビン

4
これは素晴らしくシンプルですが、非常にシンプルなバージョンスキームに依存しています。
uchuugaka 2013年

11
@ScottBerrevoets確かに、「1.2.3」が「1.1.12」(123 <1112)未満であることを意味するので、それがどのように機能するかではないことを願っています。Appleは慎重に、状態として「数値内の文字列は、数値を使用して比較しています」。つまり、文字列内の数値のセットがそれぞれ比較されます(基本的にはcomponentsSeparatedByStringアプローチ)。これを@"1.8"vs@"1.7.2.3.55"で自分でテストして、1.8が先に出てくることを確認できます。
dooleyo 2014年

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などの問題に対処するためのNathandeVriesの回答の拡張です。

まず、バージョン文字列の余分な「.0」の問題にNSStringカテゴリを付けて対処できます。

@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 deVriesによって提案された美しくシンプルなアプローチを引き続き使用できます。

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

これをNathande Vriesソリューションと組み合わせると、最良かつ最もエレガントな答えになります。
ダルマツィオ2014

これでも、7.4.2が7.5よりも高いバージョンであるとは言えませんか?
トレス2016

@Tresいいえ。NSNumericSearchがオプションとして渡されるため、文字列は数値として比較されます。したがって、7.4.2 <7.5
DonnaLea 2016

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

驚くばかり!これは、このスレッドでNSComparisonResultを使用して、7.28.2と7.28を正しく比較する唯一の例です。
CokePokes 2018

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 2015年

いいえ、4.21は21> 3として4.3より大きいと見なされます。同等性の比較を満たすために、4.21と4.30を比較する必要があります。Nathan deVriesの回答のコメントにあるディスカッションを参照してください。
スタンナー2015年

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で最も気に入っていることの1つは、ほとんどの場合、プレーン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.