เปรียบเทียบหมายเลขเวอร์ชันใน Objective-C


87

ฉันกำลังเขียนแอปพลิเคชันที่รับข้อมูลพร้อมรายการและหมายเลขเวอร์ชัน ตัวเลขมีรูปแบบเช่น "1.0.1" หรือ "1.2.5" ฉันจะเปรียบเทียบหมายเลขเวอร์ชันเหล่านี้ได้อย่างไร ฉันคิดว่าพวกเขาต้องจัดรูปแบบเป็นสตริงก่อนไม่ใช่เหรอ? ฉันต้องมีตัวเลือกอะไรบ้างเพื่อระบุว่า "1.2.5" มาหลัง "1.0.1"


ฉันเขียนไลบรารีขนาดเล็กเพื่อเปรียบเทียบ 2 เวอร์ชัน Strings ใน 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

4
นี่เป็นสิ่งที่ดีและเรียบง่าย แต่อาศัยรูปแบบเวอร์ชันที่เรียบง่ายมาก
uchuugaka

11
@ScottBerrevoets ฉันหวังว่ามันจะไม่ใช่วิธีการทำงานอย่างแน่นอนเพราะนั่นจะหมายถึง "1.2.3" น้อยกว่า "1.1.12" (123 <1112)! ตามที่ Apple ระบุอย่างรอบคอบ " Numbers ภายในสตริงจะถูกเปรียบเทียบโดยใช้ค่าตัวเลข " นั่นคือชุดของตัวเลขภายในสตริงแต่ละชุดจะถูกเปรียบเทียบกัน (โดยพื้นฐานแล้วคือcomponentsSeparatedByStringวิธีการ) คุณสามารถทดสอบด้วยตัวเองด้วย@"1.8"vs @"1.7.2.3.55"และดูว่า 1.8 ออกมาข้างหน้า
dooleyo

3
NSNumericSearch คิดว่า "1.0" น้อยกว่า "1.0.0" ไม่ค่อยยืดหยุ่นเพียงพอสำหรับวัตถุประสงค์ของฉัน
bobics

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

นี่เป็นการขยายไปสู่คำตอบของ Nathan de Vries เพื่อแก้ไขปัญหาของ 1 <1.0 <1.0.0 เป็นต้น

ก่อนอื่นเราสามารถแก้ไขปัญหาของ ".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 de Vries:

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

สิ่งนี้ควบคู่ไปกับโซลูชันของ Nathan de Vries เป็นคำตอบที่ดีและสง่างามที่สุด
Dalmazio

นี่จะยังไม่บอกว่า 7.4.2 เป็นเวอร์ชันที่สูงกว่า 7.5 หรือไม่?
Tres

@Tres no. เนื่องจาก NSNumericSearch ถูกส่งผ่านเป็นตัวเลือกสตริงจึงถูกเปรียบเทียบเป็นตัวเลขดังนั้น 7.4.2 <7.5
DonnaLea

9

ทำเองใช้ Category ..

ที่มา ..

@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

8

Sparkle (เฟรมเวิร์กการอัปเดตซอฟต์แวร์ยอดนิยมสำหรับ MacOS) มีคลาสSUStandardVersionComparatorที่ทำสิ่งนี้และยังคำนึงถึงตัวเลขบิลด์และเครื่องหมายเบต้าด้วย คือมันอย่างถูกต้องเปรียบเทียบหรือ1.0.5 > 1.0.5b7 2.0 (2345) > 2.0 (2100)รหัสใช้เฉพาะ Foundation ดังนั้นควรใช้งานได้ดีบน iOS เช่นกัน


6

ตรวจสอบหมวดหมู่ NSString ของฉันที่ใช้การตรวจสอบเวอร์ชันอย่างง่ายบน github https://github.com/stijnster/NSString-compareToVersion

สิ่งนี้จะส่งคืนNSComparisonResultซึ่งมีความแม่นยำมากขึ้นจากนั้นใช้;

นอกจากนี้ยังมีการเพิ่มผู้ช่วยเหลือ


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

AppUpdateTrackerไลบรารี iOS ของฉันมีหมวดหมู่ 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 ถือว่ามากกว่า 4.3 เท่ากับ 21> 3 เพื่อให้เป็นไปตามการเปรียบเทียบความเท่าเทียมกันของคุณคุณต้องเปรียบเทียบ 4.21 กับ 4.30 โปรดดูการอภิปรายในความคิดเห็นของนาธานเดอไวริสคำตอบของ
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.