Если вы хотите, чтобы компилятор автоматически обнаруживал символы, определенные в других файлах, вам нужно заставить программиста разместить эти файлы в предопределенных местах (например, структура пакетов Java определяет структуру папок проекта). Я предпочитаю файлы заголовков. Также вам понадобятся либо источники библиотек, которые вы используете, либо какой-то единый способ поместить информацию, необходимую компилятору в двоичные файлы.
Если вы точно знаете, какие поля вы получите, вы можете использовать один вызов sscanf ()
:
const char * stringToParse = ... ; int days, hours, minutes, seconds; Интервал NSTimeInterval; if (sscanf (stringToParse, «P% dDT% dH% dM% sS», & amp; days, & amp; hours, & amp; minutes & amp; seconds) == 4) интервал = ((дни * 24 + часы) * 60 + минуты) * 60 + секунды; иначе; // обрабатывать ошибку, синтаксический анализ не удалось
Если какое-либо из полей может быть опущено, вам нужно будет немного умнее в вашем разборе, например:
const char * stringToParse = ...; int days = 0, hours = 0, minutes = 0, seconds = 0; const char * ptr = stringToParse; while (* ptr) {if (* ptr == 'P' || * ptr == 'T') {ptr ++; Продолжать; } значение int, charsRead; тип char; if (sscanf (ptr, "% d% c% n", & amp; value, & amp; type & amp; charsRead)! = 2); // обрабатывать ошибку синтаксического анализа if (type == 'D') days = value; else if (type == 'H') hours = value; else if (type == 'M') minutes = value; else if (type == 'S') seconds = value; иначе; // обрабатывать недопустимый тип ptr + = charsRead; } Интервал NSTimeInterval = ((дни * 24 + часы) * 60 + минуты) * 60 + секунд;
Уже есть ответы, но я закончил реализацию еще одной версии, используя NSScanner
. Эта версия игнорирует год и месяц, так как они не могут быть преобразованы в число секунд.
static NSTimeInterval timeIntervalFromISO8601Duration (NSString * duration) {NSTimeInterval timeInterval = 0; NSScanner * scanner = [NSScanner scannerWithString: duration]; Обозначения NSCharacterSet * = [NSCharacterSet characterSetWithCharactersInString: @ "PYMWDTHMS"]; BOOL isScanningTime = NO; while (! [scanner isAtEnd]) {double scannedNumber = 0; BOOL didScanNumber = [сканирование scanDouble: & scannedNumber]; NSString * scanned = nil; if ([сканирование scanCharactersFromSet: указатели вString: & amp; scanned]) {if (didScanNumber) {switch ([отсканированный символAtIndex: 0]) {case 'D': timeInterval + = scannedNumber * 60 * 60 * 24; ломать; case 'H': timeInterval + = scannedNumber * 60 * 60; ломать; case 'M': if (isScanningTime) {timeInterval + = scannedNumber * 60; } ломать; case 'S': timeInterval + = scannedNumber; ломать; default: break; }} if ([scanned containsString: @ "T"]) {isScanningTime = YES; }}} return timeInterval; }
Быстрая и грязная реализация
- (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 = [dлительность компонентовSeparatedByString: 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; }
Чистая версия Objective C ...
NSString * duration = @ "P1DT10H15M49S"; int i = 0, days = 0, hours = 0, minutes = 0, seconds = 0; while (i & lt; duration.length) {NSString * str = [duration substringWithRange: NSMakeRange (i, duration.length-i)]; я ++; if ([str hasPrefix: @ "P"] || [str hasPrefix: @ "T"]) продолжить; NSScanner * sc = [NSScanner scannerWithString: str]; int value = 0; if ([sc scanInt: & amp; value]) {i + = [sc scanLocation] -1; str = [duration substringWithRange: NSMakeRange (i, duration.length-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 дней,% d часов,% d mins,% d секунд", дней, часов, минут, секунд]);
Реализация Swift2: https://github.com/Igor-Palaguta/YoutubeEngine/blob/swift-2.3/YoutubeEngine/Classes/Parser/NSDateComponents+ISO8601.swift
] Пример: let components = NSDateComponents (ISO8601String: «P1Y2M3DT4H5M6S»)
Он также корректно обрабатывает случаи «P1M» и «PT1M»
Реализация Swift3: https: // gitub.com/Igor-Palaguta/YoutubeEngine/blob/master/Source/YoutubeEngine/Parser/NSDateComponents%2BISO8601.swift
Пример: let components = dateComponents (ISO8601String: "P1Y2M3DT4H5M6S ")
Обновление 20.01.2017: добавлена поддержка недель
слегка изменяющая функция пользователя
Сергей Пекар
+ (NSString *) parseISO8601Time: (NSString *) duration {NSInteger hours = 0; NSInteger minutes = 0; NSInteger seconds = 0; // Получить часть времени из длительности отформатированного ISO 8601 http://en.wikipedia.org/wiki/ISO_8601#Durations if ([duration rangeOfString: @ "T"]. Location == NSNotFound || [dиапазон продолжительностиOfString: @ "P "] .location == NSNotFound) {NSLog (@" Время не является частью продолжительности отформатированного ISO 8601 "); return @ "0:00 Ошибка"; } duration = [duration substringFromIndex: [duration rangeOfString: @ "T"]. location]; while ([dлина продолжительности] & gt; 1) {// остается только одна буква после продолжительности синтаксического анализа = [dлительность подстрокиFromIndex: 1]; NSScanner * scanner = [[NSScanner alloc] initWithString: duration]; NSString * durationPart = [[NSString alloc] init]; [сканирование scanCharactersFromSet: [NSCharacterSet characterSetWithCharactersInString: @ "0123456789"] inString: & amp; durationPart]; Диапазон NSRangeOfDurationPart = [dиапазон продолжительностиOfString: durationPart]; if ((rangeOfDurationPart.location + rangeOfDurationPart.length) & gt; duration.length) {NSLog (@ "Время не является частью продолжительности отформатированного ISO 8601"); return @ "0:00 Ошибка"; } 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]; }
Вот быстрая версия 3 примера headkaze: этот формат был наиболее подходящим в моем случае:
private func parseISO8601Time (iso8601: String) - & gt; String {let nsISO8601 = NSString (string: iso8601) var days = 0, hours = 0, minutes = 0, seconds = 0 var i = 0 while i & lt; nsISO8601.length {var str = nsISO8601.substring (с: NSRange (location: i, length: nsISO8601.length - i)) i + = 1, если str.hasPrefix ("P") || str.hasPrefix ("T") {продолжить} пусть сканер = сканер (строка: str) значение var = 0, если scanner.scanInt (& amp; value) {i + = scanner.scanLocation - 1 str = nsISO8601.substring (с: NSRange (location: i, length: nsISO8601.length - i)) i + = 1, если str.hasPrefix ("D") {days = value} else, если str.hasPrefix ("H") {hours = value} else if str.hasPrefix ("M") {minutes = value} else, если str.hasPrefix ("S") {seconds = value}}}, если days & gt; 0 {часы + = 24 * дней}, если часы & gt; 0 {return String (формат: "% d:% 02d:% 02d", часы, минуты, секунды)} return String (формат: "% d:% 02d", минуты, секунды)}
Эта версия анализирует каждую продолжительность без ошибок. Важно: в этой версии используется ARC.
- (NSString *) parseISO8601Time: (NSString *) duration {NSInteger hours = 0; NSInteger minutes = 0; NSInteger seconds = 0; // Получить часть времени из длительности отформатированного ISO 8601. Http://en.wikipedia.org/wiki/ISO_8601#Durations duration = [duration substringFromIndex: [duration rangeOfString: @ "T"]. Location]; while ([dлина продолжительности] & gt; 1) {// остается только одна буква после продолжительности синтаксического анализа = [dлительность подстрокиFromIndex: 1]; NSScanner * scanner = [[NSScanner alloc] initWithString: duration]; NSString * durationPart = [[NSString alloc] init]; [сканирование scanCharactersFromSet: [NSCharacterSet characterSetWithCharactersInString: @ "0123456789"] inString: & amp; durationPart]; Диапазон NSRangeOfDurationPart = [dиапазон продолжительностиOfString: 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", часы, минуты, секунды]; }
Теперь в Swift
!
Управляет годами, месяцами, неделями, днями, часами, секундами и секундами!
] func convertFromISO8601Duration (isoValue: AnyObject) - & gt; Строка? {var displayString: String? var hasHitTimeSection = false var isSingular = false, если let isoString = isoValue как? String {отображаетсяString = String () для val в isoString {if val == "P" {// Не делайте ничего при разборе 'P' continue} else, если val == "T" {// Указываем, что мы имеем дело с «временной отрезок» длительности ISO8601, а затем продолжить. hasHitTimeSection = true continue} var tempString = String (), если val & gt; = "0" & amp; & amp; & amp; & amp; val & lt; = "9" {// Нам нужно знать, является ли значение сингулярным ('1') или нет ('11', '23'). если let safeDisplayedString = displayString as String! где count (displayString!) & gt; 0 & amp; & amp; val == "1" {let lastIndex = count (safeDisplayedString) - 1 let lastChar = safeDisplayedString [advance (safeDisplayedString.startIndex, lastIndex)] // проверяет, является ли текущий последний символ в отображаемой строке пробелом (""). Если это так, то мы скажем, что это сингулярно, пока не будет доказано обратное. if lastChar == "" {isSingular = true} else {isSingular = false}} else if val == "1" {// если мы просто имеем дело с '1', тогда мы скажем, что это единственное, пока не будет доказано обратное. isSingular = true} else {// ... в противном случае это множественная продолжительность. isSingular = false} tempString + = "\ (val)" отображаетсяString! + = tempString} else {// обрабатывает текст типа продолжительности. Обязательно используйте Months & amp; Минуты правильно. 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 + = "Неделя"} 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 + = "Секунды "} break default: break} // сбросить наш особый флаг, так как мы начинаем новую продолжительность. isSingular = false отображаетсяString! + = tempString}}} return displayString}
Я просмотрел эту статью в Википедии для ссылки на то, как работает ISO-8601. Я не эксперт по какао, но я уверен, что если вы сможете разобрать эту строку и извлечь компонент час, минуту, секунду, день и т. Д., То получить его в NSTimeInterval должно быть легко. Сложная часть разбирает его. Я бы сделал это примерно так:
Сначала разделите строку на две отдельные строки: одну, представляющую дни, и одну, представляющую время. NSString имеет компонент метода экземпляраSeparatedByString: NSString, который возвращает NSArray подстрок вашего исходного NSString, разделенных параметром, который вы передаете. Он будет выглядеть примерно так:
NSString * iso8601 = / * Однако вы получаете свою строку в * / NSArray * iso8601Parts = [iso8601 componentsSeparatedByString: @ "T"];
Затем выполните поиск первого элемента iso8601Parts для каждого из возможных индикаторов продолжительности дня (Y, M, W и D). Когда вы найдете его, возьмите все предыдущие цифры (и, возможно, десятичную точку), отбросьте их к плавающей точке и сохраните их где-нибудь. Помните, что если бы существовал только элемент времени, то iso8601Parts [0] будет пустой строкой.
Затем сделайте то же самое, ищите временные части во втором элементе iso8601Parts для возможных индикаторов времени (H , МИЗ). Помните, что если бы существовал только компонент дня (т. Е. В исходной строке не было символа «T»), то iso8601Parts будет иметь длину только один, а попытка доступа к второму элементу вызовет исключение из-за пределов .
NSTimeInterval - это просто длительное хранение нескольких секунд, поэтому конвертируйте отдельные фрагменты, которые вы вытащили за секунды, добавьте их вместе, сохраните их в своем NSTimeInterval, и вы настроены.
Извините, я знаю, что вы попросили «простой» способ сделать это, но, основываясь на моем (по общему признанию) поиске и знании API, это самый простой способ сделать это.