Swift: codifica l'URL


295

Se codifico una stringa come questa:

var escapedString = originalString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)

non sfugge alle barre /.

Ho cercato e trovato questo codice obiettivo C:

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

Esiste un modo più semplice per codificare un URL e, in caso contrario, come posso scriverlo in Swift?

Risposte:


613

Swift 3

In Swift 3 c'è addingPercentEncoding

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

Produzione:

Test% 2Ftest

Swift 1

In iOS 7 e versioni successive c'è stringByAddingPercentEncodingWithAllowedCharacters

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

Produzione:

Test% 2Ftest

Di seguito sono riportati utili set di caratteri (invertiti):

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

Se si desidera eseguire l'escape di un diverso set di caratteri, creare un set:
Esempio con carattere "=" aggiunto:

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

Produzione:

Test% 2Ftest% 3D42

Esempio per verificare i caratteri ASCII non presenti nel set:

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
Nessun altro è completamente sbalordito dalla durata di questo codice? Voglio dire che il nome del metodo è già molto lungo, anche senza scegliere il set di caratteri consentito.
— thatidiotguy,

38
No, preferisco la comprensibilità rispetto ai nomi brevi e concisi. Il completamento automatico elimina il dolore. stringByAddingPercentEncodingWithAllowedCharacters()lascia pochi dubbi su ciò che fa. Commento interessante considerando quanto è lunga la parola "sbalordito".
— zaph,

1
stringByAddingPercentEncodingWithAllowedCharacters (.URLHostAllowedCharacterSet ()) Non codifica correttamente tutti i caratteri La risposta di Bryan Chen è una soluzione migliore.
— Julio Garcia,

2
@zaph Ho aggiunto &al set di caratteri di URLQueryAllowedCharacterSete ho codificato ogni carattere. Controllato con iOS 9, sembra buggy, sono andato con la risposta di @ bryanchen, funziona bene !!
— Akash Kava

3
La risposta che segue che utilizza URLComponentsed URLQueryItemè IMO molto più pulita.
— Aaron Brager,

65

È possibile utilizzare URLComponents per evitare di dover codificare manualmente la percentuale della stringa di query:

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
Questa risposta richiede più attenzione, in quanto vi sono problemi con tutti gli altri (anche se per essere onesti potrebbero essere state le migliori pratiche al momento).
— Asa,

4
Purtroppo, URLQueryItemnon sempre codifica correttamente. Ad esempio, Formula+Oneverrebbe codificato in Formula+Onecui verrebbe decodificato Formula One. Quindi sii cauto con il segno più.
— Sulthan,

37

Swift 3:

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

1. encodingQuery:

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

risultato:

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

2. encodingURL:

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

risultato:

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

Ho usato la prima soluzione, ma voglio tornare al mio testo, come iOS 开发 工程师.
— Akshay Phulare,

2
Usando urlHostAllowedper codificare i parametri della query in modo errato perché non codificherà ?, =e +. Quando si codificano i parametri della query, è necessario codificare il nome e il valore del parametro separatamente e correttamente. Questo non funzionerà in un caso generale.
— Sulthan,

@Sulthan .. Hai trovato qualche soluzione / alternativa perurlHostAllowed
— Bharath

@Bharath Sì, devi creare un set di caratteri da solo, ad esempio stackoverflow.com/a/39767927/669586 o semplicemente usare URLComponents.
— Sulthan,

Anche i componenti URLC non codificano il +carattere. Quindi l'unica opzione è farlo manualmente: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
Devi includere `` (spazio) nella stringa di caratteri
— AJP

1
Devi anche includere ^
— Mani

26

Swift 4

Per codificare un parametro nell'URL trovo che usando il .alphanumericsset di caratteri l'opzione più semplice:

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

L'uso di uno dei set di caratteri standard per la codifica URL (come URLQueryAllowedCharacterSeto URLHostAllowedCharacterSet) non funzionerà, perché non escludono =né &caratteri.

Nota che utilizzando .alphanumericslo codificherà alcuni personaggi che non hanno bisogno di essere codificati (come -, ., _o ~- vedi 2.3 caratteri non numerata. In RFC 3986). Trovo che sia .alphanumericspiù semplice che costruire un set di caratteri personalizzato e non mi dispiace che alcuni caratteri aggiuntivi vengano codificati. Se questo ti dà fastidio, costruisci un set di caratteri personalizzato come descritto in Come codificare in percentuale una stringa URL , come ad esempio:

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!)"

Avvertenza: il encodedparametro è forzato da scartare. Per una stringa Unicode non valida potrebbe bloccarsi. Vedi Perché il valore restituito di String.addingPercentEncoding () è facoltativo? . Invece di scartare la forza encoded!puoi usare encoded ?? ""o usare if let encoded = ....


1
.aphanumerics ha fatto il trucco, grazie! Tutti gli altri set di caratteri non sono sfuggiti a & 's che ha causato problemi quando si usano le stringhe come parametri get.
— Dion,

14

È tutto uguale

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

// test%2Ftest

Non hai .bridgeToOvjectiveC()sostenuto il secondo argomento e non hai ottenuto "Impossibile convertire il tipo di espressione 'CFString!' digitare 'CFString!' "?
— Kreiri,

@Kreiri Perché è necessario? Sia il parco giochi che la REPL sono soddisfatti del mio codice.
— Bryan Chen,

I miei non sono: / (beta 2)
— Kreiri

1
Questa è una risposta migliore in quanto codifica e correttamente.
— Sam,

13

Swift 4:

Dipende dalle regole di codifica seguite dal tuo server.

Apple offre questo metodo di classe, ma non riporta il tipo di protocollo RCF che segue.

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

Seguendo questo utile strumento dovresti garantire la codifica di questi caratteri per i tuoi parametri:

  • $ (Dollar Sign) diventa% 24
  • & (La e commerciale) diventa% 26
  • + (Più) diventa% 2B
  • , (Virgola) diventa% 2C
  • : (Due punti) diventa% 3A
  • ; (Semi-Colon) diventa% 3B
  • = (Uguale) diventa% 3D
  • ? (Punto interrogativo) diventa% 3F
  • @ (A / At commerciale) diventa% 40

In altre parole, parlando della codifica URL, è necessario seguire il protocollo RFC 1738 .

E Swift non copre la codifica del carattere + per esempio , ma funziona bene con questi tre @:? caratteri.

Pertanto, per codificare correttamente ogni parametro, l' .urlHostAllowedopzione non è sufficiente, è necessario aggiungere anche i caratteri speciali come ad esempio:

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

Spero che questo aiuti qualcuno che diventa matto a cercare queste informazioni.


L'implementazione è completamente sbagliata. Come verrebbe codificato un parametro "věž"?
— Marián Černý

13

Swift 4 (non testato - commenta se funziona o meno. Grazie @sumizome per il suggerimento)

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

Swift 3

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

Swift 2.2 (Prendere in prestito da Zaph e correggere la chiave della query url e i valori dei parametri)

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

Esempio:

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"

Questa è una versione più breve della risposta di Bryan Chen. Immagino che urlQueryAllowedsia consentire i caratteri di controllo attraverso i quali va bene, a meno che non facciano parte della chiave o del valore nella stringa di query a che punto devono essere salvati.


2
Mi piace la soluzione Swift 3, ma non funziona per me in Swift 4: "Impossibile utilizzare un membro mutante su un valore immutabile: 'urlQueryAllowed' è una proprietà get-only".
— Marián Černý

@ MariánČerný rendi mutevole il CharacterSet (con var) e poi chiamalo .removein un secondo passaggio.
— Sumizome

Credo che questa e la maggior parte delle altre soluzioni abbiano problemi quando si applica il metodo due volte, ad esempio quando si include un URL con parametri codificati nei parametri di un altro URL.
— FD_

@FD_ conosci o hai solo un sospetto? Puoi sperimentarlo e postare indietro? In questo caso sarebbe utile includere queste informazioni. Grazie.
— AJP,

@AJP Ho appena testato tutti i tuoi frammenti. Swift 3 e 4 funzionano bene, ma quello per Swift 2.2 non codifica correttamente% 20 come% 2520.
— FD_

7

Rapido 4.2

Una soluzione rapida a una riga. Sostituisci originalStringcon la stringa che vuoi codificare.

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

Demo del parco giochi online


Questo ha funzionato grazie: puoi controllare e provare a decodificare e codificare i risultati. urldecoder.org
— Rakshitha Muranga Rodrigo l'

4

Ne avevo bisogno anch'io, quindi ho scritto un'estensione String che consente entrambe le stringhe URLEncoding, nonché l'obiettivo finale più comune, convertendo un dizionario di parametri in parametri URL in stile "GET":

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

Godere!


2
Questo non codifica il segno '&'. L'uso di un '&' in un parametro riempirà la stringa di querystring
— Sam,

Questo è sbagliato, non codifica &o =nei parametri. Controlla invece la mia soluzione.
— Marián Černý

2

Questo funziona per me.

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
}

Ho trovato la funzione sopra da questo link: http://useyourloaf.com/blog/how-to-percent-encode-a-url-string/ .


1

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


0

SWIFT 4.2

A volte questo è accaduto solo perché c'è spazio nella lumaca O assenza di codifica URL per i parametri che passano attraverso l'URL API.

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

NOTA: non dimenticare di esplorare bitmapRepresentation.


0

Questo funziona per me in Swift 5 . Il caso di utilizzo sta prendendo un URL dagli Appunti o simili che potrebbero già avere caratteri di escape ma che contiene anche caratteri Unicode che potrebbero causare URLComponentso URL(string:)fallire.

Innanzitutto, crea un set di caratteri che includa tutti i caratteri legali 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
    }
}

Successivamente, estendi Stringcon un metodo per codificare gli 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
    }

}

Che può essere testato come:

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)

Valore di urlalla fine: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

Si noti che entrambi %20e la +spaziatura sono conservati, i caratteri Unicode sono codificati, %20l'originale urlTextnon ha una doppia codifica e l'ancoraggio (frammento o #) rimane.

Modifica: ora verifica la validità di ciascun componente.


0

Per Swift 5 alla stringa del codice finale

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

Come usare ?

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

0

Nessuna di queste risposte ha funzionato per me. La nostra app si arrestava in modo anomalo quando un URL conteneva caratteri non inglesi.

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

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

A seconda dei parametri di ciò che stai cercando di fare, potresti voler creare il tuo set di caratteri. Quanto sopra consente caratteri inglesi e-._~/?%$!:

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.