Der beste Weg, um eine URL-Zeichenfolge zu analysieren, um Werte für Schlüssel zu erhalten?

81

Ich muss eine URL-Zeichenfolge wie diese analysieren:

&ad_eurl=http://www.youtube.com/video/4bL4FI1Gz6s&hl=it_IT&iv_logging_level=3&ad_flags=0&endscreen_module=http://s.ytimg.com/yt/swfbin/endscreen-vfl6o3XZn.swf&cid=241&cust_gender=1&avg_rating=4.82280613104

Ich muss den NSString in die Signleteile wie cid=241und aufteilen &avg_rating=4.82280613104. Ich habe das mit gemacht, substringWithRange:aber die Werte werden in zufälliger Reihenfolge zurückgegeben, so dass es durcheinander kommt. Gibt es eine Klasse, die eine einfache Analyse ermöglicht, in der Sie sie grundsätzlich in NSDictionary konvertieren können, um den Wert für einen Schlüssel lesen zu können (z. B. ValueForKey: cidsollte zurückgeben 241). Oder gibt es nur einen anderen einfacheren Weg, es zu analysieren, als NSMakeRangeeinen Teilstring zu erhalten?

JonasG
quelle

Antworten:

122

edit (Juni 2018): Diese Antwort ist besser . Apple hat NSURLComponentsin iOS 7 hinzugefügt .

Ich würde ein Wörterbuch erstellen und ein Array der Schlüssel / Wert-Paare mit erhalten

NSMutableDictionary *queryStringDictionary = [[NSMutableDictionary alloc] init];
NSArray *urlComponents = [urlString componentsSeparatedByString:@"&"];

Dann füllen Sie das Wörterbuch:

for (NSString *keyValuePair in urlComponents)
{
    NSArray *pairComponents = [keyValuePair componentsSeparatedByString:@"="];
    NSString *key = [[pairComponents firstObject] stringByRemovingPercentEncoding];
    NSString *value = [[pairComponents lastObject] stringByRemovingPercentEncoding];

    [queryStringDictionary setObject:value forKey:key];
}

Sie können dann mit abfragen

[queryStringDictionary objectForKey:@"ad_eurl"];

Dies ist nicht getestet, und Sie sollten wahrscheinlich weitere Fehlertests durchführen.

Thomas Joulin
quelle
13
Dies ist keine URL-Dekodierung der Komponenten. Es wird auch keine Begrenzungsprüfung für das Array pairComponents durchgeführt, bevor objectAtIndex: 1 ausgeführt wird (was ist, wenn sich am Index 1 kein Objekt befindet?).
Yetanotherjosh
8
Die Verwendung von "componentsSeparatedByString" ist nicht korrekt, um einen Schlüssel von einem Wert in einer Abfragezeichenfolge zu trennen. "=" sind häufig in Werten enthalten (z. B. Base64-codierte Token).
Donleyp
1
Eine kleine Optimierung: NSArray *urlComponents = [urlString componentsSeparatedByString:@"&"]; NSMutableDictionary *queryStringDictionary = [[NSMutableDictionary alloc] initWithCapacity: urlComponents.count];
Adnako
3
Wenn ein Wert das Zeichen "=" enthält, wird die Auffülllogik unterbrochen. Sie könnten einen Teil des Codes durchNSRange range = [keyValuePair rangeOfString:@"="]; NSString *key = [keyValuePair substringToIndex:range.location]; NSString *value = [keyValuePair substringFromIndex:range.location+1];
Renato Silva Das Neves
1
Dies erhält nicht den ersten Artikel.
Keith Adler
167

Ich habe dies auch unter https://stackoverflow.com/a/26406478/215748 beantwortet .

Sie können queryItemsin verwenden URLComponents.

Wenn Sie den Wert dieser Eigenschaft erhalten, analysiert die NSURLComponents-Klasse die Abfragezeichenfolge und gibt ein Array von NSURLQueryItem-Objekten zurück, von denen jedes ein einzelnes Schlüssel-Wert-Paar in der Reihenfolge darstellt, in der sie in der ursprünglichen Abfragezeichenfolge angezeigt werden.

let url = "http://example.com?param1=value1&param2=param2"
let queryItems = URLComponents(string: url)?.queryItems
let param1 = queryItems?.filter({$0.name == "param1"}).first
print(param1?.value)

Alternativ können Sie der URL eine Erweiterung hinzufügen, um die Arbeit zu vereinfachen.

extension URL {
    var queryParameters: QueryParameters { return QueryParameters(url: self) }
}

class QueryParameters {
    let queryItems: [URLQueryItem]
    init(url: URL?) {
        queryItems = URLComponents(string: url?.absoluteString ?? "")?.queryItems ?? []
        print(queryItems)
    }
    subscript(name: String) -> String? {
        return queryItems.first(where: { $0.name == name })?.value
    }
}

Sie können dann über den Namen auf den Parameter zugreifen.

let url = URL(string: "http://example.com?param1=value1&param2=param2")!
print(url.queryParameters["param1"])
Onato
quelle
47

Ich bin etwas spät dran, aber die bisherigen Antworten haben nicht so funktioniert, wie ich es brauchte. Sie können dieses Code-Snippet verwenden:

NSMutableDictionary *queryStrings = [[NSMutableDictionary alloc] init];
for (NSString *qs in [url.query componentsSeparatedByString:@"&"]) {
    // Get the parameter name
    NSString *key = [[qs componentsSeparatedByString:@"="] objectAtIndex:0];
    // Get the parameter value
    NSString *value = [[qs componentsSeparatedByString:@"="] objectAtIndex:1];
    value = [value stringByReplacingOccurrencesOfString:@"+" withString:@" "];
    value = [value stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

    queryStrings[key] = value;
}

Wo urlist die URL, die Sie analysieren möchten? Sie haben alle Abfragezeichenfolgen im queryStringsveränderlichen Wörterbuch maskiert.

EDIT : Swift Version:

var queryStrings = [String: String]()
if let query = url.query {
    for qs in query.componentsSeparatedByString("&") {
        // Get the parameter name
        let key = qs.componentsSeparatedByString("=")[0]
        // Get the parameter value
        var value = qs.componentsSeparatedByString("=")[1]
        value = value.stringByReplacingOccurrencesOfString("+", withString: " ")
        value = value.stringByReplacingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!

        queryStrings[key] = value
    }
}
nikolovski
quelle
2
Dies ist eine gute Antwort, aber nicht die beste. Sie sollten vorsichtig sein, wenn Parameter so etwas wie Base64-codierte Zeichenfolgen sind. Wie @donleyp sagte, kann das Zeichen '=' ein normaler Wert sein. Beispielsweise muss eine Base64-codierte Zeichenfolge mit dem Zeichen '=' aufgefüllt werden.
Khcpietro
@Aigori korrigiere mich, wenn ich falsch liege, aber es scheint mir, dass diese Antwort base64 unterstützt. =wird zum Auffüllen verwendet und erscheint =erst am Ende, wenn es dekodiert wird. Abfrageparameter ohne Wert werden jedoch nicht unterstützt.
Boris
@Boris Dekodieren Sie diese URL beispielsweise http://example.org/?base64=ZGF0YT1bMTUyLCAyNDI2LCAyMzE1XQ==mit dem Antwortcode . Es wird ZGF0YT1bMTUyLCAyNDI2LCAyMzE1XQnicht zurückkehren ZGF0YT1bMTUyLCAyNDI2LCAyMzE1XQ==. Natürlich dekodieren einige base64-Decoder korrekt, geben jedoch beispielsweise eine NSData initWithBase64EncodedString:options:leere Zeichenfolge ohne Auffüllen zurück =. Daher sollten Sie vorsichtig sein, wenn Sie mit base64-Zeichenfolgen mit Antwortcode umgehen.
Khcpietro
@Aigori ja du hast recht, aber in diesem Fall wäre die URL nicht http://example.org/?base64=ZGF0YT1bMTUyLCAyNDI2LCAyMzE1XQ==, es wäre so, http://example.org/?base64=ZGF0YT1bMTUyLCAyNDI2LCAyMzE1XQ%3D%3Dwie der Wert URL-codiert sein sollte
Boris
10

Für iOS8 und höher mit NSURLComponents:

+(NSDictionary<NSString *, NSString *> *)queryParametersFromURL:(NSURL *)url {
    NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
    NSMutableDictionary<NSString *, NSString *> *queryParams = [NSMutableDictionary<NSString *, NSString *> new];
    for (NSURLQueryItem *queryItem in [urlComponents queryItems]) {
        if (queryItem.value == nil) {
            continue;
        }
        [queryParams setObject:queryItem.value forKey:queryItem.name];
    }
    return queryParams;
}

Für iOS 8 unten:

+(NSDictionary<NSString *, NSString *> *)queryParametersFromURL:(NSURL *)url    
    NSMutableDictionary<NSString *, NSString *> * parameters = [NSMutableDictionary<NSString *, NSString *> new];
    [self enumerateKeyValuePairsFromQueryString:url.query completionblock:^(NSString *key, NSString *value) {
        parameters[key] = value;
    }];
    return parameters.copy;
}

- (void)enumerateKeyValuePairsFromQueryString:(NSString *)queryString completionBlock:(void (^) (NSString *key, NSString *value))block {
    if (queryString.length == 0) {
        return;
    }
    NSArray *keyValuePairs = [queryString componentsSeparatedByString:@"&"];
    for (NSString *pair in keyValuePairs) {
        NSRange range = [pair rangeOfString:@"="];
        NSString *key = nil;
        NSString *value = nil;

        if (range.location == NSNotFound) {
            key = pair;
            value = @"";
        }
        else {
            key = [pair substringToIndex:range.location];
            value = [pair substringFromIndex:(range.location + range.length)];
        }

        key = [self decodedStringFromString:key];
        key = key ?: @"";

        value = [self decodedStringFromString:value];
        value = value ?: @"";

        block(key, value);
    }
}

+ (NSString *)decodedStringFromString:(NSString *)string {
    NSString *input = shouldDecodePlusSymbols ? [string stringByReplacingOccurrencesOfString:@"+" withString:@" " options:NSLiteralSearch range:NSMakeRange(0, string.length)] : string;
    return [input stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
}
Yatheesha BL
quelle
6

Wenn Sie das Gleiche schnell tun möchten, können Sie eine Erweiterung verwenden.

extension NSURL {
    func queryDictionary() -> [String:String] {
        let components = self.query?.componentsSeparatedByString("&")
        var dictionary = [String:String]()

        for pairs in components ?? [] {
            let pair = pairs.componentsSeparatedByString("=")
            if pair.count == 2 {
                dictionary[pair[0]] = pair[1]
            }
        }

        return dictionary
    }
}
Apouche
quelle
4

Wenn Sie NSURLComponentsdie folgende prägnante Erweiterung verwenden, reicht dies ebenfalls aus:

extension NSURLComponents {

    func getQueryStringParameter(name: String) -> String? {
        return (self.queryItems? as [NSURLQueryItem])
            .filter({ (item) in item.name == name }).first?
            .value()
    }

}
Gilles De Mey
quelle
1
Und in Swift 2, func getQueryStringParameter(name: String) -> String? { return self.queryItems?.filter({ (item) in item.name == name }).first?.value }
Jedidja
4

Seit iOS 8 können Sie Eigenschaften direkt nameund valueweiter verwenden NSURLQueryItem.

Beispiel, wie eine URL analysiert und bestimmte Werte für einen Schlüssel in analysierten Paaren abgerufen werden.

NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:@"someURL" resolvingAgainstBaseURL:false];
NSArray *queryItems = urlComponents.queryItems;
NSMutableArray *someIDs = [NSMutableArray new];
for (NSURLQueryItem *item in queryItems) {
    if ([item.name isEqualToString:@"someKey"]) {
        [someIDs addObject:item.value];
    }
}
NSLog(@"%@", someIDs);
Jakub Truhlář
quelle
4

Swift 5

extension URL {
    func queryParams() -> [String:String] {
        let queryItems = URLComponents(url: self, resolvingAgainstBaseURL: false)?.queryItems
        let queryTuples: [(String, String)] = queryItems?.compactMap{
            guard let value = $0.value else { return nil }
            return ($0.name, value)
        } ?? []
        return Dictionary(uniqueKeysWithValues: queryTuples)
    }
}
Wujo
quelle
3

Dieser Code funktioniert in drei Fällen

1. http://www.youtube.com/watch?v=VWsl7C-y7EI&feature=youtu.be 2. http://youtu.be/lOvcFqQyaDY
3. http://www.youtube.com/watch?v= VWsl7C-y7EI

NSArray *arr = [youtubeurl componentsSeparatedByString:@"v="];
 NSString *youtubeID;
if([arr count]>0)
{
    if([arr count]==1){
        youtubeID= [[youtubeurl componentsSeparatedByString:@"/"] lastObject];

    }
    else{
        NSArray *urlComponents = [[arr objectAtIndex:1] componentsSeparatedByString:@"&"];
        youtubeID=[urlComponents objectAtIndex:0];
    }
}
Rinju Jain
quelle
1
Ich habe speziell nach einer Lösung zum Extrahieren der YouTube-ID gesucht und vergessen, die Tatsache zu berücksichtigen, dass es mehrere Variationen gültiger YouTube-URLs gibt. +1 Danke!
Seoras
3
-(NSArray *)getDataOfQueryString:(NSString *)url{
    NSArray *strURLParse = [url componentsSeparatedByString:@"?"];
    NSMutableArray *arrQueryStringData = [[NSMutableArray alloc] init];
    if ([strURLParse count] < 2) {
        return arrQueryStringData;
    }
    NSArray *arrQueryString = [[strURLParse objectAtIndex:1] componentsSeparatedByString:@"&"];

    for (int i=0; i < [arrQueryString count]; i++) {
        NSMutableDictionary *dicQueryStringElement = [[NSMutableDictionary alloc]init];
        NSArray *arrElement = [[arrQueryString objectAtIndex:i] componentsSeparatedByString:@"="];
        if ([arrElement count] == 2) {
            [dicQueryStringElement setObject:[arrElement objectAtIndex:1] forKey:[arrElement objectAtIndex:0]];
        }
        [arrQueryStringData addObject:dicQueryStringElement];
    }

    return arrQueryStringData; 
}

Sie diese Funktion nur URL übergeben und Sie erhalten alle Elemente der Abfrage.

Verrückter Entwickler
quelle
3

Eine späte Lösung hierfür in Form einer Swift 3-Erweiterung auf URL

extension URL {

  func value(for paramater: String) -> String? {

    let queryItems = URLComponents(string: self.absoluteString)?.queryItems
    let queryItem = queryItems?.filter({$0.name == paramater}).first
    let value = queryItem?.value

    return value
  }

}

Kern

Damo
quelle
2

Als volle Funktion:

+ (NSString *)getQueryComponentWithName:(NSString *)name  fromURL:(NSURL *)url{

NSString *component = nil;
if (url) {
    NSString *query = url.query;

    NSMutableDictionary *queryStringDictionary = [NSMutableDictionary dictionary];
    NSArray *urlComponents = [query componentsSeparatedByString:@"&"];

    for (NSString *keyValuePair in urlComponents){

        NSArray *pairComponents = [keyValuePair componentsSeparatedByString:@"="];
        NSString *key = [[pairComponents firstObject] stringByRemovingPercentEncoding];
        NSString *value = [[pairComponents lastObject] stringByRemovingPercentEncoding];

        [queryStringDictionary setObject:value forKey:key];
    }

    component = [queryStringDictionary objectForKey:name];
}

return component;
}
[self getQueryComponentWithName:@"example" fromURL:[NSURL URLWithString:@"https://google.es/?example=test"]];
93sauu
quelle
Sie haben einen kleinen Tippfehler am Beispiel dort - die Klammern sind abgenutzt platziert
Vaiden
2

So erhalten Sie Abfrageparameter als Diktat:

extension URL {
    var parameters: [String: String] {
        var parameters = [String: String]()
        if let urlComponents = URLComponents(url: self, resolvingAgainstBaseURL: false),
            let queryItems = urlComponents.queryItems {
            for queryItem in queryItems where queryItem.value != nil {
                parameters[queryItem.name] = queryItem.value
            }
        }
        return parameters
    }
}

oder optional zurücksenden, wenn dies in Ihrem Fall bequemer ist.

Oleksandr Buravlyov
quelle
0

Die Abfrageeigenschaft in der NSURL gibt die Abfragezeichenfolge an. Anschließend können Sie die Abfragezeichenfolge mit componentsSeparatedByString analysieren

    NSArray *parameters = [[url query] componentsSeparatedByString:@"&"];

    NSMutableDictionary *keyValuePairs = [NSMutableDictionary dictionary];

    for (NSString *eachParam in parameters)
    {
        NSArray *QryParts = [eachParam componentsSeparatedByString:@"="];
        if ( [QryParts count] == 2 )
        {
            keyValuePairs[QryParts[0]] = QryParts[1];
        }
        else
        {
            keyValuePairs[QryParts[0]] = QryParts[0];
        }
    }

NSString * name = [keyValuePairs valueForKey:@"name"];
NSString * username = [keyValuePairs valueForKey:@"username"];
Joe M.
quelle
0
- (NSString *)getLoginTokenFromUrl:(NSString *)urlString {
    NSURL *url = [NSURL URLWithString:urlString];
    NSArray *queryStrings = [url.query componentsSeparatedByString:@"&"];

    NSMutableDictionary *queryParams = [[NSMutableDictionary alloc] init];
    for (NSString *qs in queryStrings) {
        // Get the parameter name
        NSArray *components = [qs componentsSeparatedByString:@"="];
        NSString *key = [components objectAtIndex:0];

        // Get the parameter value
        NSString *value;
        if (components.count > 1) {
            value = [components objectAtIndex:1];
        }
        else {
            value = @"";
        }
        value = [value stringByReplacingOccurrencesOfString:@"+" withString:@" "];
        value = [value stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

        queryParams[key] = value;
    }

    return [queryParams objectForKey:@"login_token"];
}
Yogesh Maheshwari
quelle
0

Ein Swift 2-Ansatz:

extension NSURL {

  var queryDictionary: [String: String] {
    var queryDictionary = [String: String]()
    guard let components = NSURLComponents(URL: self, resolvingAgainstBaseURL: false), queryItems = components.queryItems else { return queryDictionary }
    queryItems.forEach { queryDictionary[$0.name] = $0.value }
    return queryDictionary
  }

}

Laden Sie Gist herunter

Scott Gardner
quelle