2009-07-18 21 views
14

मैं उद्देश्य सी में एक ISO-8601 अवधि अवधि को स्ट्रिंग करने का एक आसान तरीका ढूंढ रहा हूं। परिणाम NSTimeInterval की तरह उपयोग करने योग्य कुछ होना चाहिए।उद्देश्य सी में आईएसओ -8601 अवधि का विश्लेषण कैसे करें?

आईएसओ -8601 अवधि का एक उदाहरण: P1DT13H24M17S, जिसका अर्थ है 1 दिन, 13 घंटे, 24 मिनट और 17 सेकंड।

उत्तर

6

क्या आप जानते हैं कि वास्तव में कौन तुम हो रही हो जाएगा प्रांगण है, तो आप sscanf() में से एक मंगलाचरण का उपयोग कर सकते हैं:

const char *stringToParse = ...; 
int days, hours, minutes, seconds; 
NSTimeInterval interval; 
if(sscanf(stringToParse, "P%dDT%dH%dM%sS", &days, &hours, &minutes, &seconds) == 4) 
    interval = ((days * 24 + hours) * 60 + minutes) * 60 + seconds; 
else 
    ; // handle error, parsing failed 

क्षेत्रों के किसी भी लोप हो सकता है, तो आप एक छोटे में होशियार रहना होगा अपने पार्स, जैसे:

const char *stringToParse = ...; 
int days = 0, hours = 0, minutes = 0, seconds = 0; 

const char *ptr = stringToParse; 
while(*ptr) 
{ 
    if(*ptr == 'P' || *ptr == 'T') 
    { 
     ptr++; 
     continue; 
    } 

    int value, charsRead; 
    char type; 
    if(sscanf(ptr, "%d%c%n", &value, &type, &charsRead) != 2) 
     ; // handle parse error 
    if(type == 'D') 
     days = value; 
    else if(type == 'H') 
     hours = value; 
    else if(type == 'M') 
     minutes = value; 
    else if(type == 'S') 
     seconds = value; 
    else 
     ; // handle invalid type 

    ptr += charsRead; 
} 

NSTimeInterval interval = ((days * 24 + hours) * 60 + minutes) * 60 + seconds; 
+0

एक छोटा पार्सर लिखना बिल्कुल ठीक नहीं था जब मैंने एक आसान समाधान खोजना शुरू किया, लेकिन ऐसा लगता है कि वास्तव में कोई दूसरा रास्ता नहीं है। मैंने सोचा कि NSDateFormatter के साथ एक विशेष प्रारूप स्ट्रिंग का उपयोग करके एक चाल हो सकती है, लेकिन यह समय अंतराल के साथ काम नहीं कर रहा है। वैसे भी, मैं एक समान पार्सर लिखना समाप्त कर दिया। मदद करने के लिए सभी को धन्यवाद। – Zargony

+2

उपरोक्त कोड उदाहरण के साथ ध्यान में रखने के लिए एक छोटी सी चीज (हां, मुझे एहसास है कि यह संभवतः एक बिंदु साबित करने के लिए था, वास्तविक खपत के लिए नहीं)। आईएसओ 8601 मानक अवधि मानों को आंशिक मानों (यानी 1.5, 0.6, आदि) के रूप में निर्दिष्ट करने की अनुमति देता है जिसे "।" द्वारा सीमित किया जा सकता है। या ","। –

0

आईएसओ -8601 वास्तव में कैसे काम करता है इसके संदर्भ में मैंने इस Wikipedia article को देखा। मैं कोई कोको विशेषज्ञ नहीं हूं, लेकिन मैं सट्टेबाजी कर रहा हूं यदि आप उस स्ट्रिंग को पार्स कर सकते हैं और घटक घंटे, मिनट, दूसरा, दिन इत्यादि निकाल सकते हैं, इसे एनएसटीइम इंटरवल में ले जाना आसान होना चाहिए। मुश्किल हिस्सा इसे पार्स कर रहा है। मैं शायद ऐसा कुछ करूंगा:

सबसे पहले, स्ट्रिंग को दो अलग तारों में विभाजित करें: एक दिन का प्रतिनिधित्व करता है, और एक बार का प्रतिनिधित्व करता है। NSString एक उदाहरण विधि componentsSeparatedByString है:। NSString है कि अपने मूल पैरामीटर में पारित द्वारा अलग NSString की सबस्ट्रिंग एक NSArray रिटर्न वह कुछ इस तरह दिखेगा:

NSString* iso8601 = /*However you're getting your string in*/ 
NSArray* iso8601Parts = [iso8601 componentsSeparatedByString:@"T"]; 

अगला, प्रत्येक के लिए iso8601Parts के पहले तत्व खोज संभावित दिन अवधि संकेतक (वाई, एम, डब्ल्यू, और डी) के। जब आप एक पाते हैं, तो सभी पूर्ववर्ती अंकों (और संभवतः एक दशमलव बिंदु) को पकड़ें, उन्हें एक फ्लोट पर डालें, और उन्हें कहीं भी स्टोर करें। याद रखें कि अगर केवल एक समय तत्व था, तो iso8601Parts [0] खाली स्ट्रिंग होगी।

फिर, वही काम करें जो संभव समय संकेतक (एच, एम, एस) के लिए iso8601Parts के दूसरे तत्व में समय भागों की तलाश में है। याद रखें कि अगर केवल एक दिन घटक था (यानी, मूल स्ट्रिंग में कोई 'टी' वर्ण नहीं था), तो iso8601Partts केवल लंबाई का होगा, और दूसरे तत्व तक पहुंचने का प्रयास सीमा अपवाद से बाहर हो जाएगा ।

एक एनएसटीइमइंटरवाल कई सेकंड्स को केवल लंबे समय तक संग्रहित करता है, इसलिए अलग-अलग टुकड़ों को आप सेकेंड में खींचते हैं, उन्हें एक साथ जोड़ते हैं, उन्हें अपने एनएसटीइम इंटरवल में स्टोर करते हैं, और आप सेट हैं।

क्षमा करें, मुझे पता है कि आपने इसे करने के लिए "आसान" तरीका मांगा है, लेकिन मेरे (स्वीकार्य रूप से प्रकाश) के आधार पर और एपीआई के ज्ञान के आधार पर, यह करने का सबसे आसान तरीका है।

10

एक शुद्ध उद्देश्य सी संस्करण ...

NSString *duration = @"P1DT10H15M49S"; 

int i = 0, days = 0, hours = 0, minutes = 0, seconds = 0; 

while(i < duration.length) 
{ 
    NSString *str = [duration substringWithRange:NSMakeRange(i, duration.length-i)]; 

    i++; 

    if([str hasPrefix:@"P"] || [str hasPrefix:@"T"]) 
     continue; 

    NSScanner *sc = [NSScanner scannerWithString:str]; 
    int value = 0; 

    if ([sc scanInt:&value]) 
    { 
     i += [sc scanLocation]-1; 

     str = [duration substringWithRange:NSMakeRange(i, duration.length-i)]; 

     i++; 

     if([str hasPrefix:@"D"]) 
      days = value; 
     else if([str hasPrefix:@"H"]) 
      hours = value; 
     else if([str hasPrefix:@"M"]) 
      minutes = value; 
     else if([str hasPrefix:@"S"]) 
      seconds = value; 
    } 
} 

NSLog(@"%@", [NSString stringWithFormat:@"%d days, %d hours, %d mins, %d seconds", days, hours, minutes, seconds]); 
+0

यह पी 1 एम के लिए विफल रहता है - (इसके लिए 1 मिनट देता है इसके बजाय इसे 1 महीने वापस करना चाहिए) –

7

यह versi त्रुटियों के बिना हर यूट्यूब अवधि पार्स पर।
महत्वपूर्ण: यह संस्करण एआरसी का उपयोग करता है।

- (NSString*)parseISO8601Time:(NSString*)duration 
{ 
    NSInteger hours = 0; 
    NSInteger minutes = 0; 
    NSInteger seconds = 0; 

    //Get Time part from ISO 8601 formatted duration http://en.wikipedia.org/wiki/ISO_8601#Durations 
    duration = [duration substringFromIndex:[duration rangeOfString:@"T"].location]; 

    while ([duration length] > 1) { //only one letter remains after parsing 
     duration = [duration substringFromIndex:1]; 

     NSScanner *scanner = [[NSScanner alloc] initWithString:duration]; 

     NSString *durationPart = [[NSString alloc] init]; 
     [scanner scanCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@""] intoString:&durationPart]; 

     NSRange rangeOfDurationPart = [duration rangeOfString:durationPart]; 

     duration = [duration substringFromIndex:rangeOfDurationPart.location + rangeOfDurationPart.length]; 

     if ([[duration substringToIndex:1] isEqualToString:@"H"]) { 
      hours = [durationPart intValue]; 
     } 
     if ([[duration substringToIndex:1] isEqualToString:@"M"]) { 
      minutes = [durationPart intValue]; 
     } 
     if ([[duration substringToIndex:1] isEqualToString:@"S"]) { 
      seconds = [durationPart intValue]; 
     } 
    } 

    return [NSString stringWithFormat:@"%02d:%02d:%02d", hours, minutes, seconds]; 
} 
+0

अच्छी नौकरी। लेकिन आपको यह उल्लेख करना होगा कि इसे एआरसी पर्यावरण में चलाया जाना चाहिए। – Nekto

+0

जोड़ा गया, सलाह –

+0

के लिए धन्यवाद आप मिनटों के बजाय सेकंड गलत वर्तनी। – orkenstein

0

वहाँ जवाब पहले से ही कर रहे हैं, लेकिन मैं NSScanner का उपयोग कर अभी तक एक और संस्करण को लागू करने समाप्त हो गया। यह संस्करण वर्ष और महीने को अनदेखा करता है क्योंकि उन्हें सेकंड की संख्या में परिवर्तित नहीं किया जा सकता है।

static NSTimeInterval timeIntervalFromISO8601Duration(NSString *duration) { 
    NSTimeInterval timeInterval = 0; 
    NSScanner *scanner = [NSScanner scannerWithString:duration]; 

    NSCharacterSet *designators = [NSCharacterSet characterSetWithCharactersInString:@"PYMWDTHMS"]; 
    BOOL isScanningTime = NO; 

    while (![scanner isAtEnd]) { 
     double scannedNumber = 0; 
     BOOL didScanNumber = [scanner scanDouble:&scannedNumber]; 

     NSString *scanned = nil; 
     if ([scanner scanCharactersFromSet:designators intoString:&scanned]) { 
      if (didScanNumber) { 
       switch ([scanned characterAtIndex:0]) { 
        case 'D': 
         timeInterval += scannedNumber * 60 * 60 * 24; 
         break; 
        case 'H': 
         timeInterval += scannedNumber * 60 * 60; 
         break; 
        case 'M': 
         if (isScanningTime) { 
          timeInterval += scannedNumber * 60; 
         } 
         break; 
        case 'S': 
         timeInterval += scannedNumber; 
         break; 
        default: 
         break; 
       } 
      } 

      if ([scanned containsString:@"T"]) { 
       isScanningTime = YES; 
      } 
     } 
    } 

    return timeInterval; 
} 
1

यहाँ स्विफ्ट के लिए एक उदाहरण है:

func parseDuration(duration: String) -> Int { 

var days = 0 
var hours = 0 
var minutes = 0 
var seconds = 0 

var decisionMaker = 0 
var factor = 1 

let specifiers: [Character] = ["M", "H", "T", "P"] 

let length = count(duration) 

for i in 1...length { 

    let index = advance(duration.startIndex, length - i) 
    let char = duration[index] 

    for specifier in specifiers { 
     if char == specifier { 
      decisionMaker++ 
      factor = 1 
     } 
    } 

    if let value = String(char).toInt() { 

     switch decisionMaker { 
      case 0: 
       seconds += value * factor 
       factor *= 10 
      case 1: 
       minutes += value * factor 
       factor *= 10 
      case 2: 
       hours += value * factor 
       factor *= 10 
      case 4: 
       days += value * factor 
       factor *= 10 
      default: 
       break 
     } 
    } 

} 

return seconds + (minutes * 60) + (hours * 3600) + (days * 3600 * 24) 
} 
0

त्वरित और गंदे कार्यान्वयन (केवल घंटे, मिनट और सेकंड के लिए)

- (NSInteger)integerFromYoutubeDurationString:(NSString*)duration{ 

    if(duration == nil){ 
     return 0; 
    } 

    NSString *startConst = @"PT"; 
    NSString *hoursConst = @"H"; 
    NSString *minutesConst = @"M"; 
    NSString *secondsConst = @"S"; 
    NSString *hours = nil; 
    NSString *minutes = nil; 
    NSString *seconds = nil; 
    NSInteger totalSeconds = 0; 

    NSString *clean = [duration componentsSeparatedByString:startConst][1]; 

    if([clean containsString:hoursConst]){ 
     hours = [clean componentsSeparatedByString:hoursConst][0]; 
     clean = [clean componentsSeparatedByString:hoursConst][1]; 
     totalSeconds = [hours integerValue]*3600; 
    } 
    if([clean containsString:minutesConst]){ 
     minutes = [clean componentsSeparatedByString:minutesConst][0]; 
     clean = [clean componentsSeparatedByString:minutesConst][1]; 
     totalSeconds = totalSeconds + [minutes integerValue]*60; 
    } 
    if([clean containsString:secondsConst]){ 
     seconds = [clean componentsSeparatedByString:secondsConst][0]; 
     totalSeconds = totalSeconds + [seconds integerValue]; 
    } 

    return totalSeconds; 
} 
3

उपयोगकर्ता के थोड़ा संशोधित समारोह

सर्गेई पेकर

+ (NSString*)parseISO8601Time:(NSString*)duration 
{ 
    NSInteger hours = 0; 
    NSInteger minutes = 0; 
    NSInteger seconds = 0; 

    //Get Time part from ISO 8601 formatted duration http://en.wikipedia.org/wiki/ISO_8601#Durations 
    if ([duration rangeOfString:@"T"].location == NSNotFound || [duration rangeOfString:@"P"].location == NSNotFound) { 
     NSLog(@"Time is not a part from ISO 8601 formatted duration"); 
     return @"0:00 Error"; 
    } 

    duration = [duration substringFromIndex:[duration rangeOfString:@"T"].location]; 

    while ([duration length] > 1) { //only one letter remains after parsing 
     duration = [duration substringFromIndex:1]; 

     NSScanner *scanner = [[NSScanner alloc] initWithString:duration]; 
     NSString *durationPart = [[NSString alloc] init]; 
     [scanner scanCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@""] intoString:&durationPart]; 

     NSRange rangeOfDurationPart = [duration rangeOfString:durationPart]; 

     if ((rangeOfDurationPart.location + rangeOfDurationPart.length) > duration.length) { 
      NSLog(@"Time is not a part from ISO 8601 formatted duration"); 
      return @"0:00 Error"; 
     } 

     duration = [duration substringFromIndex:rangeOfDurationPart.location + rangeOfDurationPart.length]; 

     if ([[duration substringToIndex:1] isEqualToString:@"H"]) { 
      hours = [durationPart intValue]; 
     } 
     if ([[duration substringToIndex:1] isEqualToString:@"M"]) { 
      minutes = [durationPart intValue]; 
     } 
     if ([[duration substringToIndex:1] isEqualToString:@"S"]) { 
      seconds = [durationPart intValue]; 
     } 
    } 

    if (hours != 0) 
     return [NSString stringWithFormat:@"%ld:%02ld:%02ld", (long)hours, (long)minutes, (long)seconds]; 
    else 
     return [NSString stringWithFormat:@"%ld:%02ld", (long)minutes, (long)seconds]; 
} 
0

अब Swift में! (हाँ यह थोड़ा लंबा है, लेकिन यह सभी मामलों और एकवचन/बहुवचन को संभालता है)।

हैंडल साल, महीने, सप्ताह, दिन, घंटे, मिनट, और सेकेंड!

func convertFromISO8601Duration(isoValue: AnyObject) -> String? { 

    var displayedString: String? 
    var hasHitTimeSection = false 
    var isSingular = false 

    if let isoString = isoValue as? String { 

     displayedString = String() 

     for val in isoString { 


      if val == "P" { 
       // Do nothing when parsing the 'P' 
       continue 

      }else if val == "T" { 
       // Indicate that we are now dealing with the 'time section' of the ISO8601 duration, then carry on. 
       hasHitTimeSection = true 
       continue 
      } 

      var tempString = String() 

      if val >= "0" && val <= "9" { 

       // We need to know whether or not the value is singular ('1') or not ('11', '23'). 
       if let safeDisplayedString = displayedString as String! 
        where count(displayedString!) > 0 && val == "1" { 

        let lastIndex = count(safeDisplayedString) - 1 

        let lastChar = safeDisplayedString[advance(safeDisplayedString.startIndex, lastIndex)] 

         //test if the current last char in the displayed string is a space (" "). If it is then we will say it's singular until proven otherwise. 
        if lastChar == " " { 
         isSingular = true 
        } else { 
         isSingular = false 
        } 
       } 
       else if val == "1" { 
        // if we are just dealing with a '1' then we will say it's singular until proven otherwise. 
        isSingular = true 
       } 
       else { 
        // ...otherwise it's a plural duration. 
        isSingular = false 
       } 

       tempString += "\(val)" 

       displayedString! += tempString 

      } else { 

       // handle the duration type text. Make sure to use Months & Minutes correctly. 
       switch val { 

       case "Y", "y": 

        if isSingular { 
         tempString += " Year " 
        } else { 
         tempString += " Years " 
        } 

        break 

       case "M", "m": 

        if hasHitTimeSection { 

         if isSingular { 
          tempString += " Minute " 
         } else { 
          tempString += " Minutes " 
         } 
        } 
        else { 

         if isSingular { 
          tempString += " Month " 
         } else { 
          tempString += " Months " 
         } 
        } 

        break 

       case "W", "w": 

        if isSingular { 
         tempString += " Week " 
        } else { 
         tempString += " Weeks " 
        } 

        break 

       case "D", "d": 

        if isSingular { 
         tempString += " Day " 
        } else { 
         tempString += " Days " 
        } 

        break 

       case "H", "h": 

        if isSingular { 
         tempString += " Hour " 
        } else { 
         tempString += " Hours " 
        } 

        break 

       case "S", "s": 

        if isSingular { 
         tempString += " Second " 
        } else { 
         tempString += " Seconds " 
        } 

        break 

       default: 
        break 

       } 

       // reset our singular flag, since we're starting a new duration. 
       isSingular = false 

       displayedString! += tempString 

      } 

     } 

    } 

    return displayedString 
} 
5

Swift2 कार्यान्वयन: https://github.com/Igor-Palaguta/YoutubeEngine/blob/swift-2.3/YoutubeEngine/Classes/Parser/NSDateComponents+ISO8601.swift

उदाहरण: let components = NSDateComponents(ISO8601String: "P1Y2M3DT4H5M6S")

टेस्ट: https://github.com/Igor-Palaguta/YoutubeEngine/blob/swift-2.3/Example/Tests/ISO8601DurationTests.swift

यह भी सही ढंग से हैंडल "P1M" और "PT1M" मामलों

Swift3 कार्यान्वयन: https://github.com/Igor-Palaguta/YoutubeEngine/blob/master/Source/YoutubeEngine/Parser/NSDateComponents%2BISO8601.swift

उदाहरण: let components = dateComponents(ISO8601String: "P1Y2M3DT4H5M6S")

टेस्ट: https://github.com/Igor-Palaguta/YoutubeEngine/blob/master/Tests/YoutubeEngineTests/ISO8601DurationTests.swift

अद्यतन 2017/01/20: सप्ताह

+0

सप्ताहों के लिए समर्थन शामिल नहीं है। उदाहरण के लिए P3W3DT20H31M21 विफल हो जाएगा। –

+1

@Dougmith, प्रतिक्रिया के लिए धन्यवाद। हफ्तों के लिए समर्थन जोड़ा गया, अब आप सप्ताह –

+0

बहुत बढ़िया सप्ताह के लिए सप्ताह का उपयोग कर सकते हैं! धन्यवाद –

1

यहाँ के लिए समर्थन जोड़ा तेज है headkaze उदाहरण के 3 संस्करण: इस प्रारूप था मेरे मामले में सबसे उपयुक्त:

private func parseISO8601Time(iso8601: String) -> String { 

    let nsISO8601 = NSString(string: iso8601) 

    var days = 0, hours = 0, minutes = 0, seconds = 0 
    var i = 0 

    while i < nsISO8601.length { 

     var str = nsISO8601.substring(with: NSRange(location: i, length: nsISO8601.length - i)) 

     i += 1 

     if str.hasPrefix("P") || str.hasPrefix("T") { continue } 

     let scanner = Scanner(string: str) 
     var value = 0 

     if scanner.scanInt(&value) { 

      i += scanner.scanLocation - 1 

      str = nsISO8601.substring(with: NSRange(location: i, length: nsISO8601.length - i)) 

      i += 1 

      if str.hasPrefix("D") { 
       days = value 
      } else if str.hasPrefix("H") { 
       hours = value 
      } else if str.hasPrefix("M") { 
       minutes = value 
      } else if str.hasPrefix("S") { 
       seconds = value 
      } 
     } 
    } 

    if days > 0 { 
     hours += 24 * days 
    } 

    if hours > 0 { 
     return String(format: "%d:%02d:%02d", hours, minutes, seconds) 
    } 

    return String(format: "%d:%02d", minutes, seconds) 

} 
संबंधित मुद्दे