解析URL字符串以获取键值的最佳方法是什么?

Jon*_*asG 73 parsing cocoa-touch objective-c ios swift

我需要解析像这样的URL字符串:

&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
Run Code Online (Sandbox Code Playgroud)

我需要将NSString分成像cid=241和的部分&avg_rating=4.82280613104.我一直在这样做,substringWithRange:但值以随机顺序返回,因此混淆了它.是否有任何类允许轻松解析,您基本上可以将其转换为NSDictionary,以便能够读取键的值(例如ValueForKey:cid应该返回241).或者是否有另一种更简单的方法来解析它而不是使用NSMakeRange获取子字符串?

Ona*_*ato 159

我也在/sf/answers/1848453491/上回答了这个问题.

您可以使用queryItemsURLComponents.

当您获得此属性的值时,NSURLComponents类将解析查询字符串并返回NSURLQueryItem对象的数组,每个对象表示一个键值对,它们在原始查询字符串中出现的顺序.

斯威夫特3

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)
Run Code Online (Sandbox Code Playgroud)

斯威夫特2.3

queryItems而不是URLComponents.

目标C.

使用queryItems.

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
    }
}
Run Code Online (Sandbox Code Playgroud)


Tho*_*lin 116

编辑(2018年6月):这个答案更好.Apple NSURLComponents在iOS 7中添加了.

我会创建一个字典,获取一个键/值对的数组

NSMutableDictionary *queryStringDictionary = [[NSMutableDictionary alloc] init];
NSArray *urlComponents = [urlString componentsSeparatedByString:@"&"];
Run Code Online (Sandbox Code Playgroud)

然后填充字典:

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

    [queryStringDictionary setObject:value forKey:key];
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用查询

[queryStringDictionary objectForKey:@"ad_eurl"];
Run Code Online (Sandbox Code Playgroud)

这是未经测试的,你应该做更多的错误测试.

  • 这不是对组件进行URL解码.在执行objectAtIndex:1之前,它还没有对pairComponents数组进行边界检查(如果索引1处没有对象,该怎么办?). (13认同)
  • 使用"componentsSeparatedByString"对于从查询字符串中的值拆分键是不正确的."="经常包含在值中(例如base64编码的令牌). (8认同)
  • 如果任何值包含字符"=",则会破坏填充逻辑.您可以用`NSRange range = [keyValuePair rangeOfString:@"="]替换部分代码; NSString*key = [keyValuePair substringToIndex:range.location]; NSString*value = [keyValuePair substringFromIndex:range.location + 1];` (3认同)

Mar*_*ski 46

我有点晚了,但到目前为止提供的答案并没有按照我的需要运作.您可以使用此代码段:

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;
}
Run Code Online (Sandbox Code Playgroud)

url您要解析的URL 在哪里.您在queryStrings可变字典中拥有转义的所有查询字符串.

编辑:Swift版本:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是一个很好的答案,但不是最好的.当参数类似于base64编码的字符串时,您应该小心.正如@donleyp所说,'='字符可以是正常值.例如,base64编码的字符串需要使用'='字符填充. (2认同)

Yat*_*B L 9

适用于iOS8及以上版本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;
}
Run Code Online (Sandbox Code Playgroud)

对于iOS 8以下:

+(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];
}
Run Code Online (Sandbox Code Playgroud)


apo*_*che 6

如果你想在swift中做同样的事情,你可以使用扩展.

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
    }
}
Run Code Online (Sandbox Code Playgroud)


Wuj*_*ujo 6

斯威夫特 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)
    }
}
Run Code Online (Sandbox Code Playgroud)


Jak*_*lář 5

从 iOS 8 开始,您可以直接使用属性name和.valueNSURLQueryItem

例如,如何解析 URL 并获取解析对中键的特定值。

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);
Run Code Online (Sandbox Code Playgroud)