Swift - URL kodlama


295

Eğer böyle bir dize kodlarsanız:

var escapedString = originalString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)

eğik çizgilerden kaçmaz /.

Bu Objective C kodunu araştırdım ve buldum:

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

Bir URL'yi kodlamanın daha kolay bir yolu var mı ve değilse, bunu Swift'e nasıl yazarım?

Yanıtlar:


613

Hızlı 3

Swift 3'te addingPercentEncoding

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

Çıktı:

Test% 2Ftest

Hızlı 1

İOS 7 ve sonraki sürümlerde stringByAddingPercentEncodingWithAllowedCharacters

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

Çıktı:

Test% 2Ftest

Aşağıdakiler yararlı (ters çevrilmiş) karakter kümeleridir:

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

Farklı bir karakter kümesinin kaçmasını istiyorsanız bir küme oluşturun:
"=" karakteri eklenmiş örnek:

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

Çıktı:

Test% 2Ftest% 3D42

Kümede olmayan ascii karakterlerini doğrulama örneği:

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
Bu kodun ne kadar süreceği konusunda başka hiç kimse tamamen şaşırmış değil mi? Yani yöntem adı, izin verilen karakter kümesini seçmeden bile çok uzun.
thatidiotguy

38
Hayır, kısa kısa adlandırma konusunda anlaşılırlığı destekliyorum. Otomatik tamamlama acıyı giderir. stringByAddingPercentEncodingWithAllowedCharacters()ne yaptığı hakkında çok az şüphe bırakır. Kelimenin ne kadar uzun olduğunu düşünerek ilginç yorum: "sarkık".
zaph

1
stringByAddingPercentEncodingWithAllowedCharacters (.URLHostAllowedCharacterSet ()) Tüm karakteri düzgün şekilde kodlamaz Bryan Chen'in cevabı daha iyi bir çözümdür.
Julio Garcia

2
@zaph &Karakter setine ekledim URLQueryAllowedCharacterSetve her karakteri kodladım . İOS 9 ile kontrol edildi, buggy gibi görünüyor, @ bryanchen cevabı ile gittim, iyi çalışıyor !!
Akash Kava

3
Aşağıdaki cevap çok daha temiz IMO kullanıyor URLComponentsve kullanıyor URLQueryItem.
Aaron Brager

65

Sorgu dizenizi manuel olarak yüzde olarak kodlamak zorunda kalmamak için URLComponents kullanabilirsiniz:

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
Diğerleriyle ilgili sorunlar olduğu için bu cevabın daha fazla ilgiye ihtiyacı vardır (adil olmakla birlikte, o zaman en iyi uygulama olabilirler).
Asa

4
Ne yazık ki, URLQueryItemher zaman doğru kodlamaz. Örneğin, Formula+Onekodlanacak Formula+Oneolan kod çözülür Formula One. Bu nedenle artı işaretine dikkat edin.
Sulthan

37

Hızlı 3:

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

1. kodlama

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

sonuç:

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

2. kodlama URL'si:

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

sonuç:

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

İlk çözümü kullandım, ancak metnimi iOS 开发 工程师 gibi geri istiyorum.
Akshay Phulare

2
Kullanılması urlHostAllowedyanlış o kodlamak olmaz çünkü sorgu parametrelerini kodlamak için ?, =ve +. Sorgu parametrelerini kodlarken, parametre adını ve değerini ayrı ve doğru olarak kodlamanız gerekir. Bu genel bir durumda çalışmaz.
Sulthan

@Sulthan .. için bir çözüm / alternatif buldunuzurlHostAllowed
Bharath

@Bharath Evet, kendi başınıza bir karakter kümesi oluşturmanız gerekir, örneğin stackoverflow.com/a/39767927/669586 veya sadece kullanın URLComponents.
Sulthan

URLComponents +karakterleri de kodlamaz . Bu yüzden tek seçenek manuel olarak yapmaktır:CharacterSet.urlQueryAllowed.subtracting(CharacterSet(charactersIn: "+"))
SoftDesigner

36

Hızlı 3:

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

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

2
Karakter dizesine `` (boşluk) eklemeniz gerekir
AJP

1
Ayrıca ^
Mani

26

Hızlı 4

URL'de bir parametreyi kodlamak için .alphanumericskarakter kümesini kullanarak en kolay seçeneği buldum :

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

URL (gibi Kodlama için standart Karakter Setleri herhangi kullanılması URLQueryAllowedCharacterSetveya URLHostAllowedCharacterSetonlar dışarıda değil, çünkü değil işi) olur =ya da &karakter.

Not kullanarak bu .alphanumericskodlanmış gerekmez bazı karakterler kodlar o (gibi -, ., _veya ~- bkz . 2.3 Ayrılmamış karakterleri RFC 3986 yılında). .alphanumericsÖzel bir karakter kümesi oluşturmaktan daha basit kullanarak buluyorum ve kodlanacak bazı ek karakterleri umursamıyorum. Bu sizi rahatsız ediyorsa, örneğin bir URL Dizesini yüzde nasıl kodlar? Bölümünde açıklandığı gibi özel bir karakter seti oluşturun :

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

Uyarı:encoded Parametre kuvvet çözülen. Geçersiz unicode dizesi için kilitlenebilir. Bkz . String.addingPercentEncoding () dönüş değeri neden isteğe bağlıdır? . Kuvvet açma yerine encoded!kullanabilirsiniz encoded ?? ""veya kullanabilirsiniz if let encoded = ....


1
.aphanumerics hile yaptı, teşekkür ederim! Diğer tüm karakter kümeleri get parametresi olarak dizeleri kullanırken sorunlara neden olan & 'lardan kaçmadı.
Dion

14

Her şey aynı

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

// test%2Ftest

Atmadın .bridgeToOvjectiveC()ikinci argüman ve ifadenin türünü dönüştürülemez" alamadım 'CFString!' 'CFString!' "yazmak için?
Kreiri

@Kreiri Neden gerekli? Hem oyun alanı hem de REPL kodumdan memnun.
Bryan Chen

Benim değil: / (beta 2)
Kreiri

1
Bu doğru bir şekilde kodladığı için daha iyi bir cevaptır.
Sam

13

Hızlı 4:

Bu, sunucunuzun izlediği kodlama kurallarına bağlıdır.

Apple bu sınıf yöntemini sunuyor, ancak hangi RCF protokolünü izlediğini bildirmiyor.

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

Bu kullanışlı aracı izleyerek parametreleriniz için bu karakterlerin kodlanmasını garanti etmelisiniz:

  • $ (Dolar İşareti)% 24 olur
  • & (Ve işareti)% 26 olur
  • + (Artı)% 2B olur
  • , (Virgül)% 2C olur
  • : (İki nokta üst üste)% 3A olur
  • ; (Yarım Kolon)% 3B olur
  • = (Eşittir)% 3D olur
  • ? (Soru İşareti)% 3F olur
  • @ (Ticari A / At)% 40 olur

Başka bir deyişle, URL kodlaması hakkında konuşurken, RFC 1738 protokolünü izlemelisiniz .

Swift, örneğin + karakterin kodlamasını kapsamaz , ancak bu üç @ ile iyi çalışır:? karakter.

Bu nedenle, her bir parametrenizi doğru şekilde kodlamak için .urlHostAllowedseçenek yeterli değildir, örneğin özel karakterleri de eklemelisiniz:

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

Umarım bu, bu bilgileri aramak için deli olan birine yardımcı olur.


Uygulamanız tamamen yanlış. "Věž" parametresi nasıl kodlanır?
Marián Černý

13

Swift 4 (test edilmedi - lütfen çalışıp çalışmadığını yorumlayın. Öneri için teşekkürler @sumizome)

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

Hızlı 3

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

Swift 2.2 (Zaph'dan borçlanma ve url sorgu anahtarı ve parametre değerleri için düzeltme)

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

Misal:

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"

Bu, Bryan Chen'in cevabının daha kısa bir versiyonudur. urlQueryAllowedKontrol karakterleri hangi onlar kaçış gerekir hangi noktada anahtar veya değer sorgu dizesinde bir parçası oluşturmak sürece hangi aracılığıyla iyi izin tahmin ediyorum .


2
Swift 3 çözümünü seviyorum, ancak Swift 4'te benim için çalışmıyor: "Değişken değer değiştirilemez değerde kullanılamıyor: 'urlQueryAllowed' sadece bir özelliktir".
Marián Černý

@ MariánČerný CharacterSet'i değiştirilebilir (ile var) yapın ve sonra .removeikinci adımda çağırın .
sumizome

Bu ve diğer birçok çözüm yöntemi iki kez uygularken, örneğin başka bir URL'nin parametrelerine kodlanmış params ile bir URL dahil ederken sorunları olduğuna inanıyorum.
FD_

@FD_ biliyor musunuz ya da sadece bir önseziniz var mı? Bunu deneyebilir ve geri gönderebilir misiniz? Eğer öyleyse bu bilgiyi eklemek iyi olur. Teşekkür ederim.
AJP

@AJP Tüm snippet'lerinizi test ettim. Swift 3 ve 4 iyi çalışıyor, ancak Swift 2.2 için olan% 20'yi% 2520 olarak düzgün şekilde kodlamıyor.
FD_

7

Hızlı 4.2

Hızlı bir hat çözümü. Değiştir originalStringEğer kodlamak istediğiniz dize ile.

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

Çevrimiçi Oyun Demosu


Bu işe yaradı: sonuçları çözüp kodlamayı kontrol edebilir ve deneyebilirsiniz. urldecoder.org
Rakshitha Muranga Rodrigo

4

Bu kendime ihtiyacım vardı, bu yüzden bir parametre sözlüğü "GET" tarzı URL Parametrelerine dönüştürme hem URLEncoding dizeleri hem de daha yaygın son hedef için izin veren bir String uzantısı yazdı:

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

Zevk almak!


2
Bu '&' işaretini kodlamaz. Bir parametrede '&' kullanmak querystring'i f___ olacak
Sam

Bu yanlış, kodlamıyor &veya =parametrelerde. Bunun yerine çözümümü kontrol et.
Marián Černý

2

Bu benim için çalışıyor.

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
}

Yukarıda bu bağlantıdan fonksiyon buldum: http://useyourloaf.com/blog/how-to-percent-encode-a-url-string/ .


1

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


0

SWIFT 4.2

Bazen bu sadece slug VEYA API URL'sinden geçen parametreler için URL kodlaması olmadığında boşluk olduğu için oldu.

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

NOT: Keşfetmeyi unutmayın bitmapRepresentation.


0

Swift 5'te bu benim için çalışıyor . Kullanım durumu, panodan veya benzerlerinden, zaten kaçmış karakterlere sahip olabilecek, ancak neden olabilen URLComponentsveya URL(string:)başarısız olabilecek Unicode karakterleri içeren bir URL alıyor .

İlk olarak, tüm URL yasal karakterlerini içeren bir karakter kümesi oluşturun:

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

Ardından, StringURL'leri kodlamak için bir yöntemle genişletin :

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
    }

}

Hangi gibi test edilebilir:

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)

Sonundaki değer url: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

Her ikisinin %20ve +aralığın korunduğunu, Unicode karakterlerin kodlandığını, %20orijinalde urlTextçift ​​kodlanmadığını ve çapanın (parça veya #) kaldığını unutmayın.

Düzenle: Şimdi her bir bileşenin geçerliliğini kontrol edin.


0

Swift 5'in kod dizesini bitirmesi için

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

Nasıl kullanılır ?

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

0

Bu cevapların hiçbiri benim için işe yaramadı. Bir URL, İngilizce olmayan karakterler içerdiğinde uygulamamız çöküyordu.

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

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

Yapmaya çalıştığınız şeyin parametrelerine bağlı olarak, sadece kendi karakter setinizi oluşturmak isteyebilirsiniz. Yukarıdaki ingilizce karakterler için izin verir ve-._~/?%$!:

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.