Jak kodować ciąg znaków w adresie URL?


Odpowiedzi:


291

Niestety stringByAddingPercentEscapesUsingEncodingnie zawsze działa w 100%. Koduje znaki inne niż URL, ale pozostawia znaki zastrzeżone (takie jak ukośnik /i znak ampersand &). Najwyraźniej jest to błąd, o którym Apple wie, ale skoro go jeszcze nie naprawili, używam tej kategorii do kodowania adresu URL:

@implementation NSString (NSString_Extended)

- (NSString *)urlencode {
    NSMutableString *output = [NSMutableString string];
    const unsigned char *source = (const unsigned char *)[self UTF8String];
    int sourceLen = strlen((const char *)source);
    for (int i = 0; i < sourceLen; ++i) {
        const unsigned char thisChar = source[i];
        if (thisChar == ' '){
            [output appendString:@"+"];
        } else if (thisChar == '.' || thisChar == '-' || thisChar == '_' || thisChar == '~' || 
                   (thisChar >= 'a' && thisChar <= 'z') ||
                   (thisChar >= 'A' && thisChar <= 'Z') ||
                   (thisChar >= '0' && thisChar <= '9')) {
            [output appendFormat:@"%c", thisChar];
        } else {
            [output appendFormat:@"%%%02X", thisChar];
        }
    }
    return output;
}

Używany w ten sposób:

NSString *urlEncodedString = [@"SOME_URL_GOES_HERE" urlencode];

// Or, with an already existing string:
NSString *someUrlString = @"someURL";
NSString *encodedUrlStr = [someUrlString urlencode];

Działa to również:

NSString *encodedString = (NSString *)CFURLCreateStringByAddingPercentEscapes(
                            NULL,
                            (CFStringRef)unencodedString,
                            NULL,
                            (CFStringRef)@"!*'();:@&=+$,/?%#[]",
                            kCFStringEncodingUTF8 );

Dobra lektura na ten temat:

Cel c procent iPhone'a koduje ciąg?
Kodowanie Objective-C i Swift URL

http://cybersam.com/programming/proper-url-percent-encoding-in-ios
https://devforums.apple.com/message/15674#15674 http://simonwoodside.com/weblog/2009/4/ 22 / how_to_really_url_encode /


12
Dlaczego, u licha, miałbyś używać takiej skomplikowanej kategorii, zamiast po prostu przejść do CFURLCreateStringByAddingPercentEscapes () , gdzie możesz jawnie określić, które znaki chcesz zawsze uciec.
Lily Ballard

8
@KevinBallard, ponieważ funkcja CF działa na modelu inkluzywnym („uciec od tych znaków”), a zazwyczaj chcesz pracować na modelu ekskluzywnym („uciec od wszystkiego oprócz tych znaków”)
Dave DeLong,


31
@DaveDeLong To może być to, gdzie mam. To była standardowa kategoria we wszystkich moich projektach od około roku, więc wyobrażam sobie, że mogłeś być oryginalnym źródłem! Zredagowałem to sformułowanie, więc nie wygląda na to, że próbuję przypisać sobie zasługę za jego napisanie).
chown

4
Czemu? if (thisChar == '') {[wyjście appendString: @ "+"]; } Bez tego, jeśli będzie kodować spacje z% 20, tak jak się spodziewałam
Chris Stephens

127

To może być pomocne

NSString *sampleUrl = @"http://www.google.com/search.jsp?params=Java Developer";
NSString* encodedUrl = [sampleUrl stringByAddingPercentEscapesUsingEncoding:
 NSUTF8StringEncoding];

W przypadku iOS 7+ zalecanym sposobem jest:

NSString* encodedUrl = [sampleUrl stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];

Możesz wybrać dozwolony zestaw znaków zgodnie z wymaganiami komponentu URL.


7
To również nie koduje poprawnie separatorów parametrów, takich jak „&”, „?” w przypadku przekazywania treści do interfejsu API.
Ben Lachman

„&” nie jest prawidłowym znakiem parametru ciągu zapytania URL. Spróbuj użyć „& amp;” zamiast.
Nishant

1
stringByAddingPercentEscapesUsingEncoding jest DEPRECATED „Użyj -stringByAddingPercentEncodingWithAllowedCharacters:zamiast tego, który zawsze używa zalecanego kodowania UTF-8 i który koduje określony składnik lub podskładnik URL, ponieważ każdy składnik lub podskładnik URL ma inne reguły dotyczące prawidłowych znaków.”
Mihir Oza

89

Nowe interfejsy API zostały dodane od momentu wybrania odpowiedzi; Możesz teraz korzystać z NSURLUtilities. Ponieważ różne części adresów URL pozwalają na różne znaki, użyj odpowiedniego zestawu znaków. Poniższy przykład koduje w celu włączenia do ciągu zapytania:

encodedString = [myString stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet];

Aby skonwertować „&”, musisz usunąć go z zestawu zapytań URL lub użyć innego zestawu, ponieważ „&” jest dozwolone w zapytaniu URL:

NSMutableCharacterSet *chars = NSCharacterSet.URLQueryAllowedCharacterSet.mutableCopy;
[chars removeCharactersInRange:NSMakeRange('&', 1)]; // %26
encodedString = [myString stringByAddingPercentEncodingWithAllowedCharacters:chars];

1
Zajęło mi trochę czasu, aby zrozumieć, co masz na myśli mówiąc „użyj NSURLUtilities” - ale teraz widzę, że jest to nazwa kategorii NSString, w której Apple zdefiniowało tę nową metodę w iOS 7 i OS X 10.9.
Richard Venable

1
Tutaj jest bardziej szczegółowa odpowiedź: stackoverflow.com/a/20271177/456366
Richard Venable

Tak. Został również wymieniony jako NSURLUtilities na liście wydanych nowych interfejsów API.
Peter DeWeese

„&” jest oczywiście dozwolone w ciągu zapytania, więc zredaguję swoją odpowiedź za pomocą linku Richarda.
Peter DeWeese,

3
NSMakeRange('&', 1)nie działa w Swift, ponieważ Swift nie pozwala na konwersję znaków char bez intryg. Aby użyć tego rozwiązania w kodzie Swift, użyj removeCharactersInString("&")zamiast.removeCharactersInRange(...)
cbh2000

16

Przykład Swift 2.0 (kompatybilny z iOS 9)

extension String {

  func stringByURLEncoding() -> String? {

    let characters = NSCharacterSet.URLQueryAllowedCharacterSet().mutableCopy() as! NSMutableCharacterSet

    characters.removeCharactersInString("&")

    guard let encodedString = self.stringByAddingPercentEncodingWithAllowedCharacters(characters) else {
      return nil
    }

    return encodedString

  }

}

2
@AlecThomas, jak się wydaje, musisz skakać przez kilka obręczy, aby się tu dostać, dlatego lepiej ukryć go w przedłużeniu
Oliver Atkinson

@OliverAtkinson - dla mnie Int (String (Character („&”)))) zwraca zero, tak jak powinno. Zamiast znaków character.removeCharactersInString („&”).
ambientlight

14

Aktualizacja iOS 7

NSString *encode = [string stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];

NSString *decode = [encode stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

To jest niedokładne. Będziesz chciał inaczej uciec od różnych części adresu URL. stackoverflow.com/questions/3423545/…
Steve Moser

Właściwie to zależy od twojego serwera dla dozwolonych znaków, tutaj na moim przykładzie na serwerze, z którego korzystałem, był tylko dozwolony zestaw znaków alfanumerycznych, możesz zmienić to na określone znaki, aby zaspokoić twoje potrzeby.
Underdog

1
Pytanie dotyczy zakodowania znaku „&”.
Steve Moser

8

Zdecydowałem się użyć CFURLCreateStringByAddingPercentEscapeswywołania podanego przez zaakceptowaną odpowiedź, jednak w najnowszej wersji XCode (i IOS) wystąpił błąd, więc zamiast tego użyłem następującego:

NSString *apiKeyRaw = @"79b|7Qd.jW=])(fv|M&W0O|3CENnrbNh4}2E|-)J*BCjCMrWy%dSfGs#A6N38Fo~";

NSString *apiKey = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)apiKeyRaw, NULL, (CFStringRef)@"!*'();:@&=+$,/?%#[]", kCFStringEncodingUTF8));

To nie jest przestrzeń kodowania. Przykład @ „string string” i @ „string & string” są poprawnie zakodowane. Daj mi znać swój wkład w to samo.
Yogesh Lolusare,

2
dla mnie większość czasu spędzonego na rozwiązywaniu problemu to okres zaprzeczenia, w którym nie chciałem wierzyć, że framework nie zawiera czegoś o nazwie podobnej do „urlencode”, który robi to bez bałaganu ...
dancl

6

Spróbuj użyć stringByAddingPercentEncodingWithAllowedCharactersmetody [NSCharacterSet URLUserAllowedCharacterSet], która obejmie wszystkie przypadki

Cel C

NSString *value = @"Test / Test";
value = [value stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLUserAllowedCharacterSet]];

szybki

var value = "Test / Test"
value.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLUserAllowedCharacterSet())

Wynik

Test%20%2F%20Test


6

Po przeczytaniu wszystkich odpowiedzi na ten temat i ( niewłaściwej ) zaakceptowanej, chcę dodać swój wkład.

JEŻELI celem jest iOS7 +, a w 2017 r. Powinno, ponieważ XCode naprawdę utrudnia zapewnienie zgodności z iOS8, najlepszym sposobem, bezpiecznym wątkiem, szybkim i pełną obsługą UTF-8 w tym celu jest:

(Kod celu C)

@implementation NSString (NSString_urlencoding)

- (NSString *)urlencode {
    static NSMutableCharacterSet *chars = nil;
    static dispatch_once_t pred;

    if (chars)
        return [self stringByAddingPercentEncodingWithAllowedCharacters:chars];

    // to be thread safe
    dispatch_once(&pred, ^{
        chars = NSCharacterSet.URLQueryAllowedCharacterSet.mutableCopy;
        [chars removeCharactersInString:@"!*'();:@&=+$,/?%#[]"];
    });
    return [self stringByAddingPercentEncodingWithAllowedCharacters:chars];
}
@end

Spowoduje to rozszerzenie NSString, wykluczenie znaków zabronionych RFC, obsługę znaków UTF-8 i umożliwi korzystanie z takich rzeczy jak:

NSString *myusername = "I'm[evil]&want(to)break!!!$->àéìòù";
NSLog(@"Source: %@ -> Dest: %@", myusername, [myusername urlencode]);

Zostanie wydrukowane na konsoli debugowania:

Źródło: I'm [evil] i want (to) break !!! $ -> àéìòù -> Dest: I% 27m% 5 Goodev% 5D% 26want% 28to% 29break% 21% 21% 21% 24-% 3E% C3 % A0% C3% A9% C3% AC% C3% B2% C3% B9

... zwróć uwagę na użycie dispatch_once, aby uniknąć wielokrotnych inicjalizacji w środowiskach wielowątkowych.


5

Oto elastyczne podejście do produkcji w Swift 5.x :

public extension CharacterSet {

    static let urlQueryParameterAllowed = CharacterSet.urlQueryAllowed.subtracting(CharacterSet(charactersIn: "&?"))

    static let urlQueryDenied           = CharacterSet.urlQueryAllowed.inverted()
    static let urlQueryKeyValueDenied   = CharacterSet.urlQueryParameterAllowed.inverted()
    static let urlPathDenied            = CharacterSet.urlPathAllowed.inverted()
    static let urlFragmentDenied        = CharacterSet.urlFragmentAllowed.inverted()
    static let urlHostDenied            = CharacterSet.urlHostAllowed.inverted()

    static let urlDenied                = CharacterSet.urlQueryDenied
        .union(.urlQueryKeyValueDenied)
        .union(.urlPathDenied)
        .union(.urlFragmentDenied)
        .union(.urlHostDenied)


    func inverted() -> CharacterSet {
        var copy = self
        copy.invert()
        return copy
    }
}



public extension String {
    func urlEncoded(denying deniedCharacters: CharacterSet = .urlDenied) -> String? {
        return addingPercentEncoding(withAllowedCharacters: deniedCharacters.inverted())
    }
}

Przykładowe użycie:

print("Hello, World!".urlEncoded()!)
print("You&Me?".urlEncoded()!)
print("#Blessed 100%".urlEncoded()!)
print("Pride and Prejudice".urlEncoded(denying: .uppercaseLetters)!)

Wynik:

Hello,%20World!
You%26Me%3F
%23Blessed%20100%25
%50ride and %50rejudice

Dołącz komentarz wyjaśniający powód twojego odrzucenia, aby wiedzieć, jak napisać lepszą odpowiedź w przyszłości 🙂
Ben Leggiero

4

Użyj NSURLComponents, aby zakodować parametry HTTP GET:

    var urlComponents = NSURLComponents(string: "https://www.google.de/maps/")!
    urlComponents.queryItems = [
        NSURLQueryItem(name: "q", value: String(51.500833)+","+String(-0.141944)),
        NSURLQueryItem(name: "z", value: String(6))
    ]
    urlComponents.URL     // returns https://www.google.de/maps/?q=51.500833,-0.141944&z=6

http://www.ralfebert.de/snippets/ios/encoding-nsurl-get-parameters/


1
Nie koduje znaków ampersand, ukośników ani średników.
Fábio Oliveira

4

Ten kod pomógł mi w kodowaniu znaków specjalnych

NSString* encPassword = [password stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet alphanumericCharacterSet]];

3

Kod Swift na podstawie chown „s odpowiedź objc w tym wątku.

extension String {
    func urlEncode() -> String {
        return CFURLCreateStringByAddingPercentEscapes(
            nil,
            self,
            nil,
            "!*'();:@&=+$,/?%#[]",
            CFStringBuiltInEncodings.UTF8.rawValue
        )
    }
}

4
ta funkcja została wycofana z iOS 9 :(
Oliver Atkinson

3

W Swift 3 wypróbuj poniżej:

let stringURL = "YOUR URL TO BE ENCODE";
let encodedURLString = stringURL.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
print(encodedURLString)

Ponieważ stringByAddingPercentEscapesUsingEncodingkoduje znaki inne niż URL, ale pozostawia znaki zastrzeżone (jak !*'();:@&=+$,/?%#[]), możesz zakodować adres URL jak w następującym kodzie:

let stringURL = "YOUR URL TO BE ENCODE";
let characterSetTobeAllowed = (CharacterSet(charactersIn: "!*'();:@&=+$,/?%#[] ").inverted)
if let encodedURLString = stringURL.addingPercentEncoding(withAllowedCharacters: characterSetTobeAllowed) {
    print(encodedURLString)
}

2

W swift 3:

// exclude alpha and numeric == "full" encoding
stringUrl = stringUrl.addingPercentEncoding(withAllowedCharacters: .alphanumerics)!;

// exclude hostname and symbols &,/ and etc
stringUrl = stringUrl.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!;

2

Rada Apple w uwagach do wydania 10.11 brzmi:

Jeśli potrzebujesz procentowo zakodować cały ciąg adresu URL, możesz użyć tego kodu do zakodowania NSString, który ma być adresem URL (w urlStringToEncode):

NSString *percentEncodedURLString =
  [[NSURL URLWithDataRepresentation:[urlStringToEncode dataUsingEncoding:NSUTF8StringEncoding] relativeToURL:nil] relativeString];

wydaje się nie działać, znaki takie jak i są nadal pozostawione same sobie
kjyv

1

W moim przypadku, gdy ostatnim składnikiem były litery arabskie, zrobiłem co następuje Swift 2.2:

extension String {

 func encodeUTF8() -> String? {

    //If I can create an NSURL out of the string nothing is wrong with it
    if let _ = NSURL(string: self) {

        return self
    }

    //Get the last component from the string this will return subSequence
    let optionalLastComponent = self.characters.split { $0 == "/" }.last


    if let lastComponent = optionalLastComponent {

        //Get the string from the sub sequence by mapping the characters to [String] then reduce the array to String
        let lastComponentAsString = lastComponent.map { String($0) }.reduce("", combine: +)


        //Get the range of the last component
        if let rangeOfLastComponent = self.rangeOfString(lastComponentAsString) {
            //Get the string without its last component
            let stringWithoutLastComponent = self.substringToIndex(rangeOfLastComponent.startIndex)


            //Encode the last component
            if let lastComponentEncoded = lastComponentAsString.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.alphanumericCharacterSet()) {


            //Finally append the original string (without its last component) to the encoded part (encoded last component)
            let encodedString = stringWithoutLastComponent + lastComponentEncoded

                //Return the string (original string/encoded string)
                return encodedString
            }
        }
    }

    return nil;
}
}

stosowanie:

let stringURL = "http://xxx.dev.com/endpoint/nonLatinCharacters"

if let encodedStringURL = stringURL.encodeUTF8() {

    if let url = NSURL(string: encodedStringURL) {

      ...
    }

} 

1
-(NSString *)encodeUrlString:(NSString *)string {
  return CFBridgingRelease(
                    CFURLCreateStringByAddingPercentEscapes(
                        kCFAllocatorDefault,
                        (__bridge CFStringRef)string,
                        NULL,
                        CFSTR("!*'();:@&=+$,/?%#[]"),
                        kCFStringEncodingUTF8)
                    );
}

według następującego bloga


Ale jest przestarzałe w iOS 9. Jaki jest zaktualizowany kod tego?
Sujit Nachan,

1

Tak wiele odpowiedzi, ale dla mnie nie zadziałało, więc spróbowałem:

fun simpleServiceCall(for serviceUrl: String, appendToUrl: String) { 
    let urlString: String = serviceUrl + appendToUrl.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)!     

    let finalUrl = URL(string: urlString)!

    //continue to execute your service call... 
}

Mam nadzieję, że komuś to pomoże. dzięki


0

Dla poszczególnych parametrów zapytania zakodowanych w formularzu www utworzyłem kategorię na NSString:

- (NSString*)WWWFormEncoded{
     NSMutableCharacterSet *chars = NSCharacterSet.alphanumericCharacterSet.mutableCopy;
     [chars addCharactersInString:@" "];
     NSString* encodedString = [self stringByAddingPercentEncodingWithAllowedCharacters:chars];
     encodedString = [encodedString stringByReplacingOccurrencesOfString:@" " withString:@"+"];
     return encodedString;
}

0

// To jest bez testu

NSMutableCharacterSet* set = [[NSCharacterSet alphanumericCharacterSet] mutableCopy];
[set addCharactersInString:@"-_.~"];
NSString *encode = [test stringByAddingPercentEncodingWithAllowedCharacters:set];

0

Napotkałem podobny problem z przekazywaniem złożonych ciągów jako parametru POST. Moje ciągi znaków mogą zawierać znaki azjatyckie, spacje, cytaty i wszelkiego rodzaju znaki specjalne. Rozwiązaniem, które ostatecznie znalazłem, było przekonwertowanie mojego ciągu znaków na pasującą serię Unicode, np. „Hu0040Hu0020Hu03f5 ....” przy użyciu [NSString stringWithFormat: @ „Hu% 04x”, [string characterAtIndex: i]], aby uzyskać Unicode z każdego znak w oryginalnym ciągu. To samo można zrobić w Javie.

Ciąg ten można bezpiecznie przekazać jako parametr POST.

Po stronie serwera (PHP) zmieniam wszystkie litery „H” na „\” i przekazuję otrzymany ciąg do json_decode. Ostatnim krokiem jest uniknięcie pojedynczych cudzysłowów przed zapisaniem ciągu w MySQL.

W ten sposób mogę przechowywać dowolny ciąg UTF8 na moim serwerze.


0

Ten działa dla mnie.

func stringByAddingPercentEncodingForFormData(plusForSpace: Bool=false) -> String? {
    let unreserved = "*-._"
    let allowed = NSMutableCharacterSet.alphanumericCharacterSet()
    allowed.addCharactersInString(unreserved)

    if plusForSpace {
        allowed.addCharactersInString(" ")
    }

    var encoded = stringByAddingPercentEncodingWithAllowedCharacters(allowed)
    if plusForSpace {
        encoded = encoded?.stringByReplacingOccurrencesOfString(" ",
                                                                withString: "+")
    }
    return encoded
}

Znalazłem powyższą funkcję z tego linku: http://useyourloaf.com/blog/how-to-percent-encode-a-url-string/

Możesz również użyć tej funkcji z szybkim rozszerzeniem. Daj mi znać, jeśli jest jakiś problem.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.