Swift - koduje adres URL


295

Jeśli koduję taki ciąg:

var escapedString = originalString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)

nie ucieka przed ukośnikami /.

Szukałem i znalazłem ten kod celu C:

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

Czy istnieje łatwiejszy sposób na zakodowanie adresu URL, a jeśli nie, jak napisać to w Swift?

Odpowiedzi:


613

Szybki 3

W Swift 3 jest addingPercentEncoding

let originalString = "test/test"
let escapedString = originalString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
print(escapedString!)

Wynik:

test% 2Ftest

Szybki 1

W iOS 7 i nowszych jest stringByAddingPercentEncodingWithAllowedCharacters

var originalString = "test/test"
var escapedString = originalString.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())
println("escapedString: \(escapedString)")

Wynik:

test% 2Ftest

Przydatne (odwrócone) zestawy znaków to:

URLFragmentAllowedCharacterSet  "#%<>[\]^`{|}
URLHostAllowedCharacterSet      "#%/<>?@\^`{|}
URLPasswordAllowedCharacterSet  "#%/:<>?@[\]^`{|}
URLPathAllowedCharacterSet      "#%;<>?[\]^`{|}
URLQueryAllowedCharacterSet     "#%<>[\]^`{|}
URLUserAllowedCharacterSet      "#%/:<>?@[\]^`

Jeśli chcesz, aby inny zestaw znaków był wybierany, utwórz zestaw:
Przykład z dodanym znakiem „=”:

var originalString = "test/test=42"
var customAllowedSet =  NSCharacterSet(charactersInString:"=\"#%/<>?@\\^`{|}").invertedSet
var escapedString = originalString.stringByAddingPercentEncodingWithAllowedCharacters(customAllowedSet)
println("escapedString: \(escapedString)")

Wynik:

test% 2Ftest% 3D42

Przykład weryfikacji znaków ascii spoza zestawu:

func printCharactersInSet(set: NSCharacterSet) {
    var characters = ""
    let iSet = set.invertedSet
    for i: UInt32 in 32..<127 {
        let c = Character(UnicodeScalar(i))
        if iSet.longCharacterIsMember(i) {
            characters = characters + String(c)
        }
    }
    print("characters not in set: \'\(characters)\'")
}

6
Czy nikt inny nie ma pojęcia, jak długo ten kod ma to robić? Mam na myśli, że nazwa metody jest już długa, nawet bez wybrania dozwolonego zestawu znaków.
— thatidiotguy

38
Nie, wolę zrozumiałość niż krótkie nazewnictwo. Autouzupełnianie usuwa ból. stringByAddingPercentEncodingWithAllowedCharacters()nie pozostawia wątpliwości co do tego, co robi. Interesujący komentarz, biorąc pod uwagę, jak długie jest słowo „oszołomiony”.
— zaph

1
stringByAddingPercentEncodingWithAllowedCharacters (.URLHostAllowedCharacterSet ()) Nie koduje poprawnie wszystkich znaków Odpowiedź Bryana Chena jest lepszym rozwiązaniem.
— Julio Garcia,

2
@zaph Dodałem &do zestawu znaków URLQueryAllowedCharacterSeti każdy kod został zakodowany. Sprawdzone na iOS 9, wygląda na buggy, poszedłem z odpowiedzią @ bryanchen, działa dobrze !!
— Akash Kava

3
Poniższa odpowiedź korzysta z IMO URLComponentsi URLQueryItemjest o wiele czystsza.
— Aaron Brager,

65

Możesz użyć URLComponents, aby uniknąć konieczności ręcznego procentowego kodowania ciągu zapytania:

let scheme = "https"
let host = "www.google.com"
let path = "/search"
let queryItem = URLQueryItem(name: "q", value: "Formula One")


var urlComponents = URLComponents()
urlComponents.scheme = scheme
urlComponents.host = host
urlComponents.path = path
urlComponents.queryItems = [queryItem]

if let url = urlComponents.url {
    print(url)   // "https://www.google.com/search?q=Formula%20One"
}

extension URLComponents {
    init(scheme: String = "https",
         host: String = "www.google.com",
         path: String = "/search",
         queryItems: [URLQueryItem]) {
        self.init()
        self.scheme = scheme
        self.host = host
        self.path = path
        self.queryItems = queryItems
    }
}

let query = "Formula One"
if let url = URLComponents(queryItems: [URLQueryItem(name: "q", value: query)]).url {
    print(url)  // https://www.google.com/search?q=Formula%20One
}

7
Ta odpowiedź wymaga większej uwagi, ponieważ występują problemy ze wszystkimi pozostałymi (choć być może były to w tym czasie najlepsze praktyki).
— Asa

4
Niestety URLQueryItemnie zawsze koduje się poprawnie. Na przykład Formula+Onezostanie zakodowany do Formula+One, który zostałby zdekodowany Formula One. Dlatego bądź ostrożny ze znakiem plus.
— Sulthan

37

Swift 3:

let originalString = "http://www.ihtc.cc?name=htc&title=iOS开发工程师"

1. kodowanie Pytanie:

let escapedString = originalString.addingPercentEncoding(withAllowedCharacters:NSCharacterSet.urlQueryAllowed)

wynik:

"http://www.ihtc.cc?name=htc&title=iOS%E5%BC%80%E5%8F%91%E5%B7%A5%E7%A8%8B%E5%B8%88" 

2. kodowanieURL:

let escapedString = originalString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)

wynik:

"http:%2F%2Fwww.ihtc.cc%3Fname=htc&title=iOS%E5%BC%80%E5%8F%91%E5%B7%A5%E7%A8%8B%E5%B8%88"

Użyłem pierwszego rozwiązania, ale chcę odesłać tekst, na przykład iOS 开发 工程师.
— Akshay Phulare

2
Używanie urlHostAlloweddo kodowania parametrów zapytania jest niepoprawne, ponieważ nie koduje ?, =oraz +. Podczas kodowania parametrów zapytania musisz osobno i poprawnie zakodować nazwę i wartość parametru. To nie zadziała w ogólnym przypadku.
— Sulthan

@Sulthan .. Czy znalazłeś jakieś rozwiązanie / alternatywę dlaurlHostAllowed
— Bharath

@Bharath Tak, trzeba zbudować zestaw znaków przez siebie, np stackoverflow.com/a/39767927/669586 lub po prostu używać URLComponents.
— Sułtan

URLComponents również nie koduje +znaku. Tak więc jedyną opcją jest zrobienie tego ręcznie:CharacterSet.urlQueryAllowed.subtracting(CharacterSet(charactersIn: "+"))
— SoftDesigner

36

Swift 3:

let allowedCharacterSet = (CharacterSet(charactersIn: "!*'();:@&=+$,/?%#[] ").inverted)

if let escapedString = originalString.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) {
//do something with escaped string
}

2
Musisz dołączyć `` (spację) do ciągu znaków
— AJP

1
Musisz także dołączyć ^
— Mani

26

Szybki 4

Aby zakodować parametr w adresie URL, znajduję użycie .alphanumericszestawu znaków najłatwiejszej opcji:

let encoded = parameter.addingPercentEncoding(withAllowedCharacters: .alphanumerics)
let url = "http://www.example.com/?name=\(encoded!)"

Korzystanie ze standardowych zestawów znaków do kodowania adresów URL (takich jak URLQueryAllowedCharacterSetlub URLHostAllowedCharacterSet) nie będzie działać, ponieważ nie wykluczają =ani &znaków.

Uwaga , że używając .alphanumericsgo zakoduje pewne znaki, które nie muszą być zakodowane (jak -, ., _lub ~- patrz 2.3 znaki bezwarunkowe. W dokumencie RFC 3986). Uważam, że używanie jest .alphanumericsprostsze niż tworzenie niestandardowego zestawu znaków i nie przeszkadzają mi dodatkowe znaki do zakodowania. Jeśli Ci to przeszkadza, stwórz własny zestaw znaków, jak opisano w Jak procent kodować ciąg adresu URL , na przykład:

var allowed = CharacterSet.alphanumerics
allowed.insert(charactersIn: "-._~") // as per RFC 3986
let encoded = parameter.addingPercentEncoding(withAllowedCharacters: allowed)
let url = "http://www.example.com/?name=\(encoded!)"

Ostrzeżenie:encoded parametr jest siła rozpakowany. Niepoprawny ciąg znaków Unicode może ulec awarii. Zobacz Dlaczego wartość zwracana przez String.addingPercentEncoding () jest opcjonalna? . Zamiast wymuszać rozpakowywanie encoded!, możesz użyć encoded ?? ""lub użyć if let encoded = ....


1
.aphanumerics załatwiło sprawę, dziękuję! Wszystkie pozostałe zestawy znaków nie unikały znaków &, co powodowało problemy podczas używania ciągów jako parametrów pobierania.
— Dion

14

Wszystko jest takie samo

var str = CFURLCreateStringByAddingPercentEscapes(
    nil,
    "test/test",
    nil,
    "!*'();:@&=+$,/?%#[]",
    CFStringBuiltInEncodings.UTF8.rawValue
)

// test%2Ftest

Nie .bridgeToOvjectiveC()podałeś drugiego argumentu i nie dostałeś „Nie można przekonwertować typu wyrażenia„ CFString! ” wpisać „CFString!” ”?
— Kreiri,

@Kreiri Dlaczego jest potrzebny? Zarówno boisko, jak i REPL są zadowolone z mojego kodu.
— Bryan Chen

Moje nie są: / (beta 2)
— Kreiri

1
To lepsza odpowiedź, ponieważ koduje & poprawnie.
— Sam

13

Swift 4:

Zależy to od reguł kodowania stosowanych przez serwer.

Apple oferuje tę klasę metody, ale nie zgłasza, jakiego rodzaju protokołu RCF używa.

var escapedString = originalString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!

Postępując zgodnie z tym przydatnym narzędziem , powinieneś zagwarantować kodowanie tych znaków dla swoich parametrów:

  • $ (Znak dolara) staje się% 24
  • & (Ampersand) staje się% 26
  • + (Plus) staje się% 2B
  • , (Przecinek) staje się% 2C
  • : (Dwukropek) staje się% 3A
  • ; (Średnik dwukropek) staje się% 3B
  • = (Równa się) staje się% 3D
  • ? (Znak zapytania) staje się% 3F
  • @ (Komercyjne A / At) staje się% 40

Innymi słowy, mówiąc o kodowaniu adresów URL, powinieneś postępować zgodnie z protokołem RFC 1738 .

Swift nie obejmuje na przykład kodowania znaku + , ale działa dobrze z tymi trzema @:? znaki

Tak więc, aby poprawnie zakodować każdy parametr, .urlHostAllowedopcja nie jest wystarczająca, należy również dodać znaki specjalne, jak na przykład:

encodedParameter = parameter.replacingOccurrences(of: "+", with: "%2B")

Mam nadzieję, że to pomoże komuś, kto oszalał, przeszukać te informacje.


Twoje wdrożenie jest całkowicie błędne. W jaki sposób kodowany byłby parametr „věž”?
— Marián Černý

13

Swift 4 (nie testowany - skomentuj, czy działa, czy nie. Dziękujemy @ sumizome za sugestię)

var allowedQueryParamAndKey = NSCharacterSet.urlQueryAllowed
allowedQueryParamAndKey.remove(charactersIn: ";/?:@&=+$, ")
paramOrKey.addingPercentEncoding(withAllowedCharacters: allowedQueryParamAndKey)

Szybki 3

let allowedQueryParamAndKey =  NSCharacterSet.urlQueryAllowed.remove(charactersIn: ";/?:@&=+$, ")
paramOrKey.addingPercentEncoding(withAllowedCharacters: allowedQueryParamAndKey)

Swift 2.2 (Pożyczanie od Zapha i poprawianie klucza kwerendy adresu URL i wartości parametrów)

var allowedQueryParamAndKey =  NSCharacterSet(charactersInString: ";/?:@&=+$, ").invertedSet
paramOrKey.stringByAddingPercentEncodingWithAllowedCharacters(allowedQueryParamAndKey)

Przykład:

let paramOrKey = "https://some.website.com/path/to/page.srf?a=1&b=2#top"
paramOrKey.addingPercentEncoding(withAllowedCharacters: allowedQueryParamAndKey)
// produces:
"https%3A%2F%2Fsome.website.com%2Fpath%2Fto%2Fpage.srf%3Fa%3D1%26b%3D2%23top"

To jest krótsza wersja odpowiedzi Bryana Chena. Wydaje mi się, że urlQueryAllowedpozwala to na kontrolowanie znaków, o ile nie stanowią one części klucza lub wartości w ciągu zapytania, w którym to momencie trzeba ich uciec.


2
Podoba mi się rozwiązanie Swift 3, ale nie działa dla mnie w Swift 4: „Nie można użyć mutującego elementu o niezmiennej wartości:„ urlQueryAllowed ”jest właściwością tylko do pobrania”.
— Marián Černý

@ MariánČerný wystarczy ustawić zmienną CharacterSet (z var), a następnie wywołać .removeją w drugim kroku.
— sumizome

Wierzę, że to i większość innych rozwiązań ma problemy przy dwukrotnym zastosowaniu metody, np. Przy włączaniu adresu URL z zakodowanymi parametrami do parametrów innego adresu URL.
— FD_

@FD_ czy wiesz czy masz przeczucie? Czy możesz z tym eksperymentować i wysyłać wiadomości z powrotem? Dobrze byłoby dołączyć te informacje, jeśli tak. Dziękuję Ci.
— AJP,

@AJP Właśnie przetestowałem wszystkie twoje fragmenty. Swift 3 i 4 działają dobrze, ale ten dla Swift 2.2 nie koduje poprawnie% 20 jako% 2520.
— FD_

7

Szybki 4.2

Szybkie rozwiązanie dla jednej linii. Zastąp originalStringciągiem, który chcesz zakodować.

var encodedString = originalString.addingPercentEncoding(withAllowedCharacters: CharacterSet(charactersIn: "!*'();:@&=+$,/?%#[]{} ").inverted)

Demo placu zabaw online


Działa to dzięki: możesz sprawdzić i spróbować dekodować i kodować wyniki. urldecoder.org
— Rakshitha Muranga Rodrigo

4

Sam tego potrzebowałem, więc napisałem rozszerzenie String, które pozwala zarówno na łańcuchy URLEncoding, jak i na bardziej powszechny cel końcowy, konwersję słownika parametrów na adres URL w stylu „GET” Parametry:

extension String {
    func URLEncodedString() -> String? {
        var escapedString = self.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
        return escapedString
    }
    static func queryStringFromParameters(parameters: Dictionary<String,String>) -> String? {
        if (parameters.count == 0)
        {
            return nil
        }
        var queryString : String? = nil
        for (key, value) in parameters {
            if let encodedKey = key.URLEncodedString() {
                if let encodedValue = value.URLEncodedString() {
                    if queryString == nil
                    {
                        queryString = "?"
                    }
                    else
                    {
                        queryString! += "&"
                    }
                    queryString! += encodedKey + "=" + encodedValue
                }
            }
        }
        return queryString
    }
}

Cieszyć się!


2
To nie koduje znaku „&”. Użycie „&” w parametrze spowoduje zakłócenie kwerendy
— Sam

To źle, nie koduje &ani =parametrów. Zamiast tego sprawdź moje rozwiązanie.
— Marián Černý

2

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/ .


1

let Url = URL(string: urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")


0

SWIFT 4.2

Czasami dzieje się tak tylko dlatego, że jest miejsce w ślimaku LUB brak kodowania adresu URL dla parametrów przechodzących przez adres URL interfejsu API.

let myString = self.slugValue
                let csCopy = CharacterSet(bitmapRepresentation: CharacterSet.urlPathAllowed.bitmapRepresentation)
                let escapedString = myString!.addingPercentEncoding(withAllowedCharacters: csCopy)!
                //always "info:hello%20world"
                print(escapedString)

UWAGA: Nie zapomnij o zbadaniu bitmapRepresentation.


0

To działa dla mnie w Swift 5 . Przypadek użycia polega na pobieraniu adresu URL ze schowka lub podobnego, który mógł już mieć znaki ucieczki, ale który zawiera także znaki Unicode, które mogą powodować URLComponentslub URL(string:)nie działać.

Najpierw utwórz zestaw znaków zawierający wszystkie znaki zgodne z adresem URL:

extension CharacterSet {

    /// Characters valid in at least one part of a URL.
    ///
    /// These characters are not allowed in ALL parts of a URL; each part has different requirements. This set is useful for checking for Unicode characters that need to be percent encoded before performing a validity check on individual URL components.
    static var urlAllowedCharacters: CharacterSet {
        // Start by including hash, which isn't in any set
        var characters = CharacterSet(charactersIn: "#")
        // All URL-legal characters
        characters.formUnion(.urlUserAllowed)
        characters.formUnion(.urlPasswordAllowed)
        characters.formUnion(.urlHostAllowed)
        characters.formUnion(.urlPathAllowed)
        characters.formUnion(.urlQueryAllowed)
        characters.formUnion(.urlFragmentAllowed)

        return characters
    }
}

Następnie rozszerz Stringmetodę kodowania adresów URL:

extension String {

    /// Converts a string to a percent-encoded URL, including Unicode characters.
    ///
    /// - Returns: An encoded URL if all steps succeed, otherwise nil.
    func encodedUrl() -> URL? {        
        // Remove preexisting encoding,
        guard let decodedString = self.removingPercentEncoding,
            // encode any Unicode characters so URLComponents doesn't choke,
            let unicodeEncodedString = decodedString.addingPercentEncoding(withAllowedCharacters: .urlAllowedCharacters),
            // break into components to use proper encoding for each part,
            let components = URLComponents(string: unicodeEncodedString),
            // and reencode, to revert decoding while encoding missed characters.
            let percentEncodedUrl = components.url else {
            // Encoding failed
            return nil
        }

        return percentEncodedUrl
    }

}

Które można przetestować jak:

let urlText = "https://www.example.com/폴더/search?q=123&foo=bar&multi=eggs+and+ham&hangul=한글&spaced=lovely%20spam&illegal=<>#top"
let url = encodedUrl(from: urlText)

Wartość urlna końcu:https://www.example.com/%ED%8F%B4%EB%8D%94/search?q=123&foo=bar&multi=eggs+and+ham&hangul=%ED%95%9C%EA%B8%80&spaced=lovely%20spam&illegal=%3C%3E#top

Zauważ, że zachowane są oba znaki %20i +odstępy, znaki Unicode są kodowane, %20oryginał urlTextnie jest podwójnie kodowany, a kotwica (fragment lub #) pozostaje.

Edycja: teraz sprawdzanie ważności każdego komponentu.


0

Dla Swift 5 do końca łańcucha znaków

func escape(string: String) -> String {
    let allowedCharacters = string.addingPercentEncoding(withAllowedCharacters: CharacterSet(charactersIn: ":=\"#%/<>?@\\^`{|}").inverted) ?? ""
    return allowedCharacters
}

Jak używać ?

let strEncoded = self.escape(string: "http://www.edamam.com/ontologies/edamam.owl#recipe_e2a1b9bf2d996cbd9875b80612ed9aa4")
print("escapedString: \(strEncoded)")

0

Żadna z tych odpowiedzi nie działała dla mnie. Nasza aplikacja ulegała awarii, gdy adres URL zawierał znaki inne niż angielskie.

 let unreserved = "-._~/?%$!:"
 let allowed = NSMutableCharacterSet.alphanumeric()
     allowed.addCharacters(in: unreserved)

 let escapedString = urlString.addingPercentEncoding(withAllowedCharacters: allowed as CharacterSet)

W zależności od parametrów tego, co próbujesz zrobić, możesz po prostu stworzyć własny zestaw znaków. Powyższe pozwala na angielskie znaki i-._~/?%$!:

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.