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 =&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.