Cách tốt nhất để phân tích cú pháp chuỗi URL để nhận giá trị cho các khóa?


81

Tôi cần phân tích cú pháp chuỗi URL như sau:

&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

Tôi cần chia NSString thành các phần ký hiệu như cid=241&avg_rating=4.82280613104. Tôi đã làm điều này với substringWithRange:nhưng các giá trị trả về theo một thứ tự ngẫu nhiên, vì vậy nó làm rối tung nó lên. Có lớp nào cho phép phân tích cú pháp dễ dàng mà về cơ bản bạn có thể chuyển đổi nó thành NSDictionary để có thể đọc giá trị của một khóa (ví dụ: ValueForKey: cidshould return 241). Hay chỉ có một cách khác dễ dàng hơn để phân tích cú pháp nó hơn là sử dụng NSMakeRangeđể lấy một chuỗi con?

Câu trả lời:


121

chỉnh sửa (tháng 6 năm 2018): câu trả lời này tốt hơn . Apple đã thêm NSURLComponentsvào iOS 7 .

Tôi sẽ tạo một từ điển, lấy một mảng các cặp khóa / giá trị với

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

Sau đó điền từ điển:

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

    [queryStringDictionary setObject:value forKey:key];
}

Sau đó, bạn có thể truy vấn với

[queryStringDictionary objectForKey:@"ad_eurl"];

Điều này chưa được kiểm tra và bạn có thể nên thực hiện thêm một số kiểm tra lỗi.


13
Đây không phải là URL giải mã các thành phần. Nó cũng không thực hiện kiểm tra giới hạn trên mảng pairComponents trước khi thực hiện objectAtIndex: 1 (nếu không có đối tượng nào ở chỉ mục 1 thì sao?).
Yeta Anotherjosh

8
sử dụng "componentsSeparatedByString" không đúng để tách khóa khỏi giá trị trong chuỗi truy vấn. "=" thường được bao gồm trong các giá trị (ví dụ: mã thông báo được mã hóa base64).
donleyp 22/1013

1
Tối ưu hóa một chút: NSArray *urlComponents = [urlString componentsSeparatedByString:@"&"]; NSMutableDictionary *queryStringDictionary = [[NSMutableDictionary alloc] initWithCapacity: urlComponents.count];
adnako 12/1213

3
Nếu bất kỳ giá trị nào chứa ký tự "=", nó sẽ phá vỡ logic điền. Bạn có thể thay thế một phần của mã bởiNSRange range = [keyValuePair rangeOfString:@"="]; NSString *key = [keyValuePair substringToIndex:range.location]; NSString *value = [keyValuePair substringFromIndex:range.location+1];
Renato Silva Das Neves

1
Điều này không nhận được mục đầu tiên.
Keith Adler

166

Tôi cũng đã trả lời điều này tại https://stackoverflow.com/a/26406478/215748 .

Bạn có thể dùng queryItems trong URLComponents.

Khi bạn nhận được giá trị của thuộc tính này, lớp NSURLComponents sẽ phân tích chuỗi truy vấn và trả về một mảng các đối tượng NSURLQueryItem, mỗi đối tượng đại diện cho một cặp khóa-giá trị duy nhất, theo thứ tự mà chúng xuất hiện trong chuỗi truy vấn ban đầu.

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)

Ngoài ra, bạn có thể thêm tiện ích mở rộng trên URL để làm mọi thứ dễ dàng hơn.

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
    }
}

Sau đó, bạn có thể truy cập tham số theo tên của nó.

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

47

Tôi hơi muộn, nhưng các câu trả lời được cung cấp cho đến bây giờ không hoạt động như tôi cần. Bạn có thể sử dụng đoạn mã này:

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;
}

Trong trường hợp urllà URL mà bạn muốn phân tích. Bạn có tất cả các chuỗi truy vấn, đã thoát, trong queryStringstừ điển có thể thay đổi.

CHỈNH SỬA : Phiên bản 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
    }
}

2
Đây là câu trả lời tốt, nhưng không phải là tốt nhất. Bạn nên cẩn thận khi các tham số giống như chuỗi được mã hóa base64. Như @donleyp đã nói, ký tự '=' có thể là giá trị bình thường. Ví dụ: chuỗi được mã hóa base64 cần đệm bằng ký tự '='.
khcpietro

@Aigori sửa cho tôi nếu tôi sai, nhưng với tôi thì có vẻ như câu trả lời này hỗ trợ base64. =được sử dụng để đệm sẽ không xuất hiện =cho đến khi được giải mã ở cuối. Tuy nhiên, nó không hỗ trợ các tham số truy vấn không có giá trị.
Boris

@Boris Ví dụ: giải mã URL này http://example.org/?base64=ZGF0YT1bMTUyLCAyNDI2LCAyMzE1XQ==bằng mã trả lời. Nó sẽ ZGF0YT1bMTUyLCAyNDI2LCAyMzE1XQkhông trở lại ZGF0YT1bMTUyLCAyNDI2LCAyMzE1XQ==. Tất nhiên, một số bộ giải mã base64 giải mã chính xác, nhưng, ví dụ, NSData initWithBase64EncodedString:options:trả về chuỗi trống không có phần đệm =. Do đó, bạn nên cẩn thận khi xử lý chuỗi base64 với mã trả lời.
khcpietro 14/08/17

@Aigori có bạn nói đúng, nhưng trong trường hợp này url sẽ không được http://example.org/?base64=ZGF0YT1bMTUyLCAyNDI2LCAyMzE1XQ==, nó sẽ là http://example.org/?base64=ZGF0YT1bMTUyLCAyNDI2LCAyMzE1XQ%3D%3Dnhư giá trị nên được url mã hóa
Boris

10

Đối với iOS8 trở lên sử dụng 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;
}

Đối với iOS 8 bên dưới:

+(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];
}

6

Nếu bạn muốn làm điều tương tự một cách nhanh chóng, bạn có thể sử dụng tiện ích mở rộng.

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
    }
}

4

Nếu bạn đang sử dụng NSURLComponentstiện ích mở rộng ngắn gọn sau đây cũng sẽ thực hiện thủ thuật:

extension NSURLComponents {

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

}

1
Và trong Swift 2, func getQueryStringParameter(name: String) -> String? { return self.queryItems?.filter({ (item) in item.name == name }).first?.value }
Jedidja

4

Kể từ iOS 8, bạn có thể trực tiếp sử dụng thuộc tính namevaluebật NSURLQueryItem.

Ví dụ, cách phân tích cú pháp URL và nhận các giá trị cụ thể cho một khóa trong các cặp được phân tích cú pháp.

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

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)
    }
}

3

Mã này hoạt động trong ba trường hợp

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

1
Tôi đặc biệt đang tìm giải pháp để trích xuất ID YouTube và tôi đã quên tính đến thực tế là có nhiều biến thể của URL youtube hợp lệ. +1 Cảm ơn bạn!
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; 
}

Bạn thực hiện chức năng này chỉ cần Truyền URL và bạn sẽ nhận được tất cả các phần tử của chuỗi truy vấn.


3

Một giải pháp muộn cho vấn đề này dưới dạng một tiện ích mở rộng Swift 3 trên 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
  }

}

ý chính


2

Như đầy đủ chức năng:

+ (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"]];

Bạn có một chút typo ở ví dụ ở đó - khung được đặt worngly
Vaiden

2

Để nhận các tham số truy vấn dưới dạng chính tả:

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
    }
}

hoặc trả lại Tùy chọn nếu điều này thuận tiện hơn trong trường hợp của bạn.


0

Thuộc tính truy vấn trong NSURL sẽ cung cấp cho chuỗi truy vấn. Sau đó, bạn có thể phân tích cú pháp chuỗi truy vấn bằng cách sử dụng componentsSeparatedByString

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

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"];
}

0

Một cách tiếp cận Swift 2:

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
  }

}

Tải xuống Gist

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.