NSURL извлекает одно значение для ключа в строке параметров

У меня есть NSURL:

вызов сервера?x=a&y=b&z=c

Каков самый быстрый и эффективный способ получить значение y?

Спасибо


person Dave    schedule 09.02.2010    source источник


Ответы (13)


ОБНОВЛЕНИЕ:

Похоже, что с 2010 года, когда это было написано, Apple выпустила набор инструментов для этой цели. Пожалуйста, смотрите ответы ниже для тех.

Стандартное решение:

Ну, я знаю, вы сказали "самый быстрый способ", но после того, как я начал делать тест с NSScanner, я просто не мог остановиться. И хотя это не самый короткий путь, он, безусловно, удобен, если вы планируете часто использовать эту функцию. Я создал класс URLParser, который получает эти переменные, используя класс NSScanner. Использование простое, как:

URLParser *parser = [[[URLParser alloc] initWithURLString:@"http://blahblahblah.com/serverCall?x=a&y=b&z=c&flash=yes"] autorelease];
NSString *y = [parser valueForVariable:@"y"];
NSLog(@"%@", y); //b
NSString *a = [parser valueForVariable:@"a"];
NSLog(@"%@", a); //(null)
NSString *flash = [parser valueForVariable:@"flash"];
NSLog(@"%@", flash); //yes

И класс, который это делает, выглядит следующим образом (*исходные файлы внизу поста):

URLParser.h

@interface URLParser : NSObject {
    NSArray *variables;
}

@property (nonatomic, retain) NSArray *variables;

- (id)initWithURLString:(NSString *)url;
- (NSString *)valueForVariable:(NSString *)varName;

@end

URLParser.m

@implementation URLParser
@synthesize variables;

- (id) initWithURLString:(NSString *)url{
    self = [super init];
    if (self != nil) {
        NSString *string = url;
        NSScanner *scanner = [NSScanner scannerWithString:string];
        [scanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString:@"&?"]];
        NSString *tempString;
        NSMutableArray *vars = [NSMutableArray new];
        [scanner scanUpToString:@"?" intoString:nil];       //ignore the beginning of the string and skip to the vars
        while ([scanner scanUpToString:@"&" intoString:&tempString]) {
            [vars addObject:[tempString copy]];
        }
        self.variables = vars;
        [vars release];
    }
    return self;
}

- (NSString *)valueForVariable:(NSString *)varName {
    for (NSString *var in self.variables) {
        if ([var length] > [varName length]+1 && [[var substringWithRange:NSMakeRange(0, [varName length]+1)] isEqualToString:[varName stringByAppendingString:@"="]]) {
            NSString *varValue = [var substringFromIndex:[varName length]+1];
            return varValue;
        }
    }
    return nil;
}

- (void) dealloc{
    self.variables = nil;
    [super dealloc];
}

@end

*если вам не нравится копировать и вставлять, вы можете просто загрузить исходные файлы - я сделал короткую запись в блоге об этом здесь.

person Dimitris    schedule 09.02.2010
comment
+1 за крутость! NSScanner — это класс, с которым я мало играл, и он выглядит очень интересно. Единственный комментарий, который я хотел бы сказать, это не вызывать метод getValue.... Это подразумевает (согласно соглашению), что вы собираетесь возвращать значение через выходной параметр. valueForVariable: было бы правильным именем. - person Dave DeLong; 09.02.2010
comment
Я тоже раньше не играл с NSScanner, поэтому решил, что это хорошая задача, чтобы протестировать его. Что касается названия метода, оно мне тоже не понравилось, но было 2 часа ночи, и я хотел подвести итоги: P Оно обновлено. - person Dimitris; 09.02.2010
comment
Спасибо за этот многоразовый класс - person iOSAppDev; 28.05.2013
comment
Вы создали какой-либо репозиторий с открытым исходным кодом для этого? вместе с какаоподами? - person Janak Nirmal; 25.07.2013
comment
Не работает. https://www.youtube.com/watch?feature=player_embedded&v=Bci1eZFoyEg каждый раз вылетает с ошибкой: *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<DDURLParser 0x8d37bf0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key v. - person Doug Smith; 06.12.2013
comment
Ваш ответ заслуживает внимания, но зачем изобретать велосипед? NSURLComponents специально создан для этого. Он знает, как разбить URL-адрес на составные части, знает об элементах запроса, знает, какие части нужно экранировать/не экранировать, и делает все это за вас. Плюс это проверенный код. Ответ @anders - гораздо лучший ответ. - person Duncan C; 27.10.2016
comment
Спасибо @DuncanC, я не знаю, существовали ли NSURLComponents в 2010 году, когда это было написано. Если бы Apple реализовала это сейчас, это определенно был бы лучший подход. - person Dimitris; 04.12.2016
comment
О господи, это старая тема! Похоже, NSURLComponents был добавлен в iOS 7, поэтому я думаю, что он был недоступен, когда вы писали свой ответ. - person Duncan C; 04.12.2016

Здесь так много настраиваемых парсеров URL, помните, что NSURLComponents — ваш друг!

Вот пример, где я вытаскиваю закодированный параметр URL для «страницы».

Свифт

let myURL = "www.something.com?page=2"

var pageNumber : Int?
if let queryItems = NSURLComponents(string: myURL)?.queryItems {
    for item in queryItems {
        if item.name == "page" {
           if let itemValue = item.value {
               pageNumber = Int(itemValue)
           }
        }
    }
}
print("Found page number: \(pageNumber)")

Цель-C

NSString *myURL = @"www.something.com?page=2";
NSURLComponents *components = [NSURLComponents componentsWithString:myURL];
NSNumber *page = nil;
for(NSURLQueryItem *item in components.queryItems)
{
    if([item.name isEqualToString:@"page"])
        page = [NSNumber numberWithInteger:item.value.integerValue];
}

"Зачем изобретать велосипед!" - Кто-то умный

person anders    schedule 28.03.2015
comment
На самом деле я не понимаю, почему этот ответ не помечен как исправленный. Спасибо @anders. - person Alexander Perechnev; 27.05.2016
comment
@AlexKrzyżanowski, опоздал на игру :P - person anders; 27.05.2016
comment
Немного компактнее: пусть pageNumber: Int? = Int(NSURLComponents(string: myURL.absoluteString ?? )?.queryItems?.filter { $0.name == page }.first?.value ?? NaN) - person Edwin Vermeer; 30.09.2016
comment
+1 при использовании NSURLComponents. (проголосовали) Он позаботится о многих вещах для вас. Гораздо лучше, чем делать это самому. - person Duncan C; 28.10.2016
comment
Эдвин Вермеер и Нефарианблэк. Однако спасибо за предложения, хотя мой код может быть чрезмерно многословным, вероятно, его можно оптимизировать и, безусловно, можно сжать, БОЛЕЕ важно, чтобы примеры кода были максимально понятными. Такого же отношения следует придерживаться и в профессиональной среде. Вы всегда должны сосредоточиться на написании чистого кода, который не будет путать ваших коллег; это не соревнование и не полезно для продуктивности команды, если ваш код не читается с первого взгляда. Читабельность ВСЕГДА должна цениться больше, чем компактность вашего кода. - person anders; 16.12.2016
comment
Если вы хотите что-то более лаконичное в if let, вы можете сделать это: if let queryItems = NSURLComponents(url: url, resolvingAgainstBaseURL: true)?.queryItems, let code = queryItems.filter({ $0.name == "page" }).reduce(nil, { $1 })?.value { /*Blah blah, do stuff with code variable here/* } Это не совсем так читабельно, но вы можете использовать именованные переменные вместо анонимных переменных, если хотите сделать его немного более разборчивым. Я просто подумал, что это было полезно, так что вы можете получить результат в if-let. - person stuckj; 30.10.2018

Я почти уверен, что вы должны разобрать его самостоятельно. Тем не менее, это не так уж плохо:

NSString * q = [myURL query];
NSArray * pairs = [q componentsSeparatedByString:@"&"];
NSMutableDictionary * kvPairs = [NSMutableDictionary dictionary];
for (NSString * pair in pairs) {
  NSArray * bits = [pair componentsSeparatedByString:@"="];
  NSString * key = [[bits objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
  NSString * value = [[bits objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
  [kvPairs setObject:value forKey:key];
}

NSLog(@"y = %@", [kvPairs objectForKey:@"y"]);
person Dave DeLong    schedule 09.02.2010
comment
Вы должны использовать NSUTF8StringEncoding, а не NSASCIIStringEncoding, если только вы не ненавидите людей, не говорящих по-английски. - person Jakob Egger; 02.04.2013
comment
Он прав, знаете ли, Дэйв, хотя это вопрос кодировки, которую решила использовать рассматриваемая схема URL. Практически для всего, что есть, это UTF-8, к счастью, достаточно - person Mike Abdullah; 10.01.2014
comment
Спасибо, что сохранили честность 3-летней давности :) - person Dave DeLong; 10.01.2014

В Swift вы можете использовать NSURLComponents для анализа строки запроса NSURL в [AnyObject].

Затем вы можете создать из него словарь (или получить доступ к элементам напрямую), чтобы получить пары ключ/значение. В качестве примера это то, что я использую для анализа URL-адреса переменной NSURL:

let urlComponents = NSURLComponents(URL: url, resolvingAgainstBaseURL: false)
let items = urlComponents?.queryItems as [NSURLQueryItem]
var dict = NSMutableDictionary()
for item in items{
    dict.setValue(item.value, forKey: item.name)
}
println(dict["x"])
person leafcutter    schedule 04.02.2015
comment
Это должен быть принятый ответ сейчас: принятый в настоящее время ответ был заменен. Этот подход подходит не только для Swift; Objective-C также может использовать NSURLComponents. Он доступен с iOS 7: nshipster.com/nsurl. - person SeanR; 12.05.2015

Я использовал эту категорию: https://github.com/carlj/NSURL-Parameters.

Он маленький и простой в использовании:

#import "NSURL+Parameters.h"
...
NSURL *url = [NSURL URLWithString:@"http://foo.bar.com?paramA=valueA&paramB=valueB"];
NSString *paramA = url[@"paramA"];
NSString *paramB = url[@"paramB"];
person Troy    schedule 09.01.2014

Вы можете использовать Google Toolbox для Mac. Он добавляет в NSString функцию для преобразования строки запроса в словарь.

http://code.google.com/p/google-toolbox-for-mac/

Работает как часы

        NSDictionary * d = [NSDictionary gtm_dictionaryWithHttpArgumentsString:[[request URL] query]];
person ventayol    schedule 09.11.2011

Вот расширение Swift 2.0, которое обеспечивает простой доступ к параметрам:

extension NSURL {
    var params: [String: String] {
        get {
            let urlComponents = NSURLComponents(URL: self, resolvingAgainstBaseURL: false)
            var items = [String: String]()
            for item in urlComponents?.queryItems ?? [] {
                items[item.name] = item.value ?? ""
            }
            return items
        }
    }
} 

Пример использования:

let url = NSURL(string: "http://google.com?test=dolphins")
if let testParam = url.params["test"] {
    print("testParam: \(testParam)")
}
person Albert Bori    schedule 27.05.2016
comment
Отличное расширение, хорошо написанное, простое и отлично работает! - person Unome; 07.06.2016

Я написал простую категорию для расширения NSString/NSURL, которая позволяет извлекать параметры запроса URL по отдельности или в виде словаря пар ключ/значение:

https://github.com/nicklockwood/RequestUtils

person Nick Lockwood    schedule 06.09.2012

Я сделал это, используя метод категории, основанный на решении @Dimitris.

#import "NSURL+DictionaryValue.h"

@implementation NSURL (DictionaryValue)
-(NSDictionary *)dictionaryValue
{
NSString *string =  [[self.absoluteString stringByReplacingOccurrencesOfString:@"+" withString:@" "]
                     stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSScanner *scanner = [NSScanner scannerWithString:string];
[scanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString:@"&?"]];

NSString *temp;
NSMutableDictionary *dict = [[[NSMutableDictionary alloc] init] autorelease];
[scanner scanUpToString:@"?" intoString:nil];       //ignore the beginning of the string and skip to the vars
while ([scanner scanUpToString:@"&" intoString:&temp]) 
{
    NSArray *parts = [temp componentsSeparatedByString:@"="];
    if([parts count] == 2)
    {
        [dict setObject:[parts objectAtIndex:1] forKey:[parts objectAtIndex:0]];
    }
}

return dict;
}
@end
person odyth    schedule 26.07.2012
comment
+1: я согласен, что это лучше подходит для NSDictionary, чем для NSArray. Однако, если вы собираетесь использовать эту функцию, убедитесь, что вы присваиваете это значение ivar... т.е. NSDictionary *paramsDict = [myURL dictionaryValue]... вы не хотите создавать этот словарь снова и снова, чтобы получить значение каждого параметра. .. ;П - person JRG-Developer; 02.04.2013

Все текущие ответы зависят от версии или излишне расточительны. Зачем создавать словарь, если вам нужно только одно значение?

Вот простой ответ, который поддерживает все версии iOS:

- (NSString *)getQueryParam:(NSString *)name  fromURL:(NSURL *)url
{
    if (url)
    {
        NSArray *urlComponents = [url.query componentsSeparatedByString:@"&"];
        for (NSString *keyValuePair in urlComponents)
        {
            NSArray *pairComponents = [keyValuePair componentsSeparatedByString:@"="];
            NSString *key = [[pairComponents firstObject] stringByRemovingPercentEncoding];

            if ([key isEqualToString:name])
            {
                return [[pairComponents lastObject] stringByRemovingPercentEncoding];
            }
        }
    }
    return nil;
}
person IanS    schedule 30.06.2016

Вы можете сделать это легко:

- (NSMutableDictionary *) getUrlParameters:(NSURL *) url
{
    NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
    NSString *tmpKey = [url query];
    for (NSString *param in [[url query] componentsSeparatedByString:@"="])
    {
        if ([tmpKey rangeOfString:param].location == NSNotFound)
        {
            [params setValue:param forKey:tmpKey];
            tmpKey = nil;
        }
        tmpKey = param;
    }
    [tmpKey release];

    return params;
}

Он возвращает такой словарь: Key = value

person tryp    schedule 28.02.2013

Я немного отредактировал код Димитриса для лучшего управления памятью и повышения эффективности. Кроме того, он работает в ARC.

URLParser.h

@interface URLParser : NSObject

- (void)setURLString:(NSString *)url;
- (NSString *)valueForVariable:(NSString *)varName;

@end

URLParser.m

#import "URLParser.h"

@implementation URLParser {
    NSMutableDictionary *_variablesDict;
}

- (void)setURLString:(NSString *)url {
    [_variablesDict removeAllObjects];

    NSString *string = url;
    NSScanner *scanner = [NSScanner scannerWithString:string];
    [scanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString:@"&?"]];
    NSString *tempString;

    [scanner scanUpToString:@"?" intoString:nil];       //ignore the beginning of the string and skip to the vars
    while ([scanner scanUpToString:@"&" intoString:&tempString]) {
        NSString *dataString = [tempString copy];
        NSArray *sepStrings = [dataString componentsSeparatedByString:@"="];
        if ([sepStrings count] == 2) {
            [_variablesDict setValue:sepStrings[1] forKeyPath:sepStrings[0]];
        }
    }
}

- (id)init
{
    self = [super init];
    if (self) {
        _variablesDict = [[NSMutableDictionary alloc] init];
    }
    return self;
}

- (NSString *)valueForVariable:(NSString *)varName {
    NSString *val = [_variablesDict valueForKeyPath:varName];
    return val;
    return nil;
}

-(NSString *)description {
    return [NSString stringWithFormat:@"Current Variables: %@", _variablesDict];
}

@end
person Chilly    schedule 07.04.2014

Самый быстрый это:

NSString* x = [url valueForQueryParameterKey:@"x"];
person Malcolm Hall    schedule 01.08.2010