키 값을 얻기 위해 URL 문자열을 구문 분석하는 가장 좋은 방법은 무엇입니까?


81

다음과 같은 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

나는 같은 signle 부분에있는 NSString을 분할해야 cid=241하고 &avg_rating=4.82280613104. 나는 이것을 해왔 substringWithRange:지만 값이 임의의 순서로 반환되므로 엉망이됩니다. 기본적으로 키 값을 읽을 수 있도록 NSDictionary로 변환 할 수있는 쉬운 구문 분석을 허용하는 클래스가 있습니까 (예 : ValueForKey : cid반환해야 함 241). 아니면 NSMakeRange부분 문자열을 얻는 데 사용하는 것보다 구문 분석하는 또 다른 쉬운 방법이 있습니까?

답변:


122

편집 (2018 년 6 월) : 이 답변이 더 좋습니다 . Apple NSURLComponents은 iOS 7에 추가 되었습니다 .

사전을 만들고 키 / 값 쌍의 배열을 가져옵니다.

NSMutableDictionary *queryStringDictionary = [[NSMutableDictionary alloc] init];
NSArray *urlComponents = [urlString 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];
}

그런 다음 다음을 사용하여 쿼리 할 수 ​​있습니다.

[queryStringDictionary objectForKey:@"ad_eurl"];

이것은 테스트되지 않았으며 아마도 더 많은 오류 테스트를 수행해야 할 것입니다.


13
이것은 구성 요소의 URL 디코딩이 아닙니다. 또한 objectAtIndex : 1을 수행하기 전에 pairComponents 배열에 대한 경계 검사를 수행하지 않습니다 (인덱스 1에 객체가 없으면 어떻게됩니까?).
Yetanotherjosh

8
"componentsSeparatedByString"을 사용하는 것은 쿼리 문자열의 값에서 키를 분할하는 데 올바르지 않습니다. "="는 값에 자주 포함됩니다 (예 : base64로 인코딩 된 토큰).
donleyp

1
약간의 최적화 : NSArray *urlComponents = [urlString componentsSeparatedByString:@"&"]; NSMutableDictionary *queryStringDictionary = [[NSMutableDictionary alloc] initWithCapacity: urlComponents.count];
adnako 2013

3
값에 "="문자가 포함되어 있으면 채우기 논리가 중단됩니다. 다음과 같은 방법으로 코드의 일부를 대체 할 수NSRange range = [keyValuePair rangeOfString:@"="]; NSString *key = [keyValuePair substringToIndex:range.location]; NSString *value = [keyValuePair substringFromIndex:range.location+1];
레나토 실바 다스 네 베스

1
이것은 첫 번째 항목을 얻지 못합니다.
Keith Adler

167

나는 또한 https://stackoverflow.com/a/26406478/215748 에서 대답했습니다 .

queryItems에서 사용할 수 있습니다 URLComponents.

이 속성의 값을 가져 오면 NSURLComponents 클래스는 쿼리 문자열을 구문 분석하고 NSURLQueryItem 개체의 배열을 반환합니다. 각 개체는 원래 쿼리 문자열에 나타나는 순서대로 단일 키-값 쌍을 나타냅니다.

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)

또는 URL에 확장을 추가하여 작업을 더 쉽게 할 수 있습니다.

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

그런 다음 이름으로 매개 변수에 액세스 할 수 있습니다.

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

47

조금 늦었지만 지금까지 제공된 답변이 필요에 따라 작동하지 않았습니다. 이 코드 조각을 사용할 수 있습니다.

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

url구문 분석 할 URL은 어디에 있습니까 ? queryStrings변경 가능한 사전 에 이스케이프 처리 된 모든 쿼리 문자열이 있습니다 .

편집 : 스위프트 버전 :

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
이것은 좋은 대답이지만 최선은 아닙니다. 매개 변수가 base64로 인코딩 된 문자열과 같은 경우에는주의해야합니다. @donleyp이 말했듯이 '='문자는 정상적인 값이 될 수 있습니다. 예를 들어 base64로 인코딩 된 문자열에는 '='문자를 사용한 패딩이 필요합니다.
khcpietro

@Aigori 내가 틀렸다면 나를 정정하지만이 답변은 base64를 지원하는 것 같습니다. =패딩에 사용되는 =것은 끝에 디코딩 될 때까지 표시되지 않습니다 . 그러나 값이없는 쿼리 매개 변수는 지원하지 않습니다.
Boris

@Boris 예를 들어이 URL http://example.org/?base64=ZGF0YT1bMTUyLCAyNDI2LCAyMzE1XQ==을 응답 코드로 디코딩 합니다. 반환 ZGF0YT1bMTUyLCAyNDI2LCAyMzE1XQ되지 않습니다 ZGF0YT1bMTUyLCAyNDI2LCAyMzE1XQ==. 물론 일부 base64 디코더는 올바르게 디코딩되지만, 예를 들어 NSData initWithBase64EncodedString:options:padding없이 빈 문자열을 반환합니다 =. 따라서 응답 코드로 base64 문자열을 처리 할 때주의해야합니다.
khcpietro

@Aigori 그래 네 말이 맞아,하지만 URL이되지 않을 것이 경우 http://example.org/?base64=ZGF0YT1bMTUyLCAyNDI2LCAyMzE1XQ==, 그것은 것입니다 http://example.org/?base64=ZGF0YT1bMTUyLCAyNDI2LCAyMzE1XQ%3D%3D값이 URL 인코딩해야으로
보리스

10

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

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

6

신속하게 동일한 작업을 수행하려면 확장 프로그램을 사용할 수 있습니다.

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

NSURLComponents다음과 같은 간결한 확장을 사용 하는 경우 에도 트릭을 수행합니다.

extension NSURLComponents {

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

}

1
그리고 스위프트 2, func getQueryStringParameter(name: String) -> String? { return self.queryItems?.filter({ (item) in item.name == name }).first?.value }
Jedidja

4

아이폰 OS 8 년부터 직접 속성을 사용할 수 있습니다 namevalue에를 NSURLQueryItem.

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

4

스위프트 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

이 코드는 세 가지 경우에 작동합니다.

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
저는 특별히 YouTube ID를 추출하는 솔루션을 찾고 있었는데 유효한 YouTube URL의 여러 변형이 있다는 사실을 고려하는 것을 잊었습니다. +1 감사합니다!
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; 
}

이 함수는 URL을 전달하고 쿼리 문자열의 모든 요소를 ​​얻습니다.


3

URL에 대한 Swift 3 확장의 형태로 이에 대한 늦은 솔루션

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
  }

}

요점


2

전체 기능으로 :

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

당신이 예에서 약간의 오타가 - 괄호가 worngly 배치
Vaiden

2

쿼리 매개 변수를 dict로 가져 오려면 :

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

또는 귀하의 경우에 더 편리한 경우 선택 사항을 반환하십시오.


0

NSURL의 쿼리 속성은 쿼리 문자열을 제공합니다. 그런 다음 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

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
  }

}

Gist 다운로드

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.