質問

ISO-8601 を含む文字列を解析する簡単な方法を探しています。 間隔 目標Cで。結果は次のように使えるものになるはずです NSTimeInterval.

ISO-8601 の期間の例: P1DT13H24M17S, 、これは 1 日と 13 時間と 24 分と 17 秒を意味します。

役に立ちましたか?

解決

あなたが取得することがありますフィールドを正確にわかっている場合、あなたはsscanf()の1つの呼び出しを使用することができます:

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;

他のヒント

純粋なObjective Cのバージョン...

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]);

このバージョンでは、エラーなしですべてのユーチューブ時間を解析。
重要:のこのバージョンの使用ARC

- (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:@"0123456789"] 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];
}

スイフト2 実装: 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」ケースも正しく処理します。

スイフト3 実装: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 年 1 月 20 日更新:数週間にわたるサポートを追加

ユーザーのわずかに変更する機能

セルゲイPekar

+ (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:@"0123456789"] 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];
}

ここでは、迅速ための例です。 (唯一の時間、分、秒)

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)
}
このフォーマットは、私の場合は、最も適した::

このここ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)

}

そこの答えはすでにありますが、私は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;
}

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
}

スイフト4.2バージョン

は、年、月、日、時間、分、秒で動作します。 秒はフロート番号を指定できます。

extension String{
    public func parseISO8601Time() -> Duration {

        let nsISO8601 = NSString(string: self)

        var days = 0, hours = 0, minutes = 0, seconds: Float = 0, weeks = 0, months = 0, years = 0
        var i = 0

        var beforeT:Bool = true

        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") {
                beforeT = !str.hasPrefix("T")
                continue
            }

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

            if scanner.scanFloat(&value) {

                i += scanner.scanLocation - 1

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

                i += 1

                if str.hasPrefix("Y") {
                    years = Int(value)
                } else if str.hasPrefix("M") {
                    if beforeT{
                        months = Int(value)
                    }else{
                        minutes = Int(value)
                    }
                } else if str.hasPrefix("W") {
                    weeks = Int(value)
                } else if str.hasPrefix("D") {
                    days = Int(value)
                } else if str.hasPrefix("H") {
                    hours = Int(value)
                } else if str.hasPrefix("S") {
                    seconds = value
                }
            }
        }

        return Duration(years: years, months: months, weeks: weeks, days: days, hours: hours, minutes: minutes, seconds: seconds)
}      

期間のstructます:

public struct Duration {

let daysInMonth: Int = 30
let daysInYear: Int = 365

var years: Int
var months: Int
var weeks: Int
var days: Int
var hours: Int
var minutes: Int
var seconds: Float

public func getMilliseconds() -> Int{
    return Int(round(seconds*1000)) + minutes*60*1000 + hours*60*60*1000 + days*24*60*60*1000 + weeks*7*24*60*60*1000 + months*daysInMonth*24*60*60*1000 + years*daysInYear*24*60*60*1000
}

public func getFormattedString() -> String{

    var formattedString = ""

    if years != 0{
        formattedString.append("\(years)")
        formattedString.append(" ")
        formattedString.append(years == 1 ? "year".localized() : "years".localized())
        formattedString.append(" ")
    }

    if months != 0{
        formattedString.append("\(months)")
        formattedString.append(" ")
        formattedString.append(months == 1 ? "month".localized() : "months".localized())
        formattedString.append(" ")
    }

    if weeks != 0{
        formattedString.append("\(weeks)")
        formattedString.append(" ")
        formattedString.append(weeks == 1 ? "week".localized() : "weeks".localized())
        formattedString.append(" ")
    }

    if days != 0{
        formattedString.append("\(days)")
        formattedString.append(" ")
        formattedString.append(days == 1 ? "day".localized() : "days".localized())
        formattedString.append(" ")
    }

    if seconds != 0{
        formattedString.append(String(format: "%02d:%02d:%.02f", hours, minutes, seconds))
    }else{
        formattedString.append(String(format: "%02d:%02d", hours, minutes))
    }

    return formattedString
}

}

scroll top