URL penyandian cepat

295

Jika saya menyandikan string seperti ini:

var escapedString = originalString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)

itu tidak luput dari tebasan /.

Saya telah mencari dan menemukan kode Objective C ini:

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

Apakah ada cara yang lebih mudah untuk menyandikan URL dan jika tidak, bagaimana cara menulis ini di Swift?

MegaCookie
sumber

Jawaban:

613

Cepat 3

Di Swift 3 ada addingPercentEncoding

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

Keluaran:

test% 2Fest

Cepat 1

Di iOS 7 dan di atasnya ada stringByAddingPercentEncodingWithAllowedCharacters

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

Keluaran:

test% 2Fest

Berikut ini adalah rangkaian karakter yang berguna (terbalik):

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

Jika Anda ingin satu set karakter yang berbeda diloloskan, buat satu set:
Contoh dengan menambahkan "=" karakter:

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

Keluaran:

test% 2Test% 3D42

Contoh untuk memverifikasi karakter ascii tidak di 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)\'")
}
zaph
sumber
6
Apakah tidak ada orang lain yang benar-benar terperangah pada berapa lama kode ini untuk melakukan ini? Maksud saya nama metode sudah sangat panjang, bahkan tanpa memilih set karakter yang diizinkan.
thatidiotguy
38
Tidak, saya lebih menyukai pengertian daripada penamaan singkat. Autocomplete menghilangkan rasa sakit. stringByAddingPercentEncodingWithAllowedCharacters()meninggalkan sedikit keraguan tentang apa yang dilakukannya. Komentar menarik mengingat berapa lama kata: "terperangah" adalah.
zaph
1
stringByAddingPercentEncodingWithAllowedCharacters (.URLHostAllowedCharacterSet ()) Tidak menyandikan semua karakter dengan benar Jawaban Bryan Chen adalah solusi yang lebih baik.
Julio Garcia
2
@ zaph Saya menambahkan &ke set karakter URLQueryAllowedCharacterSetdan saya membuat setiap karakter dikodekan. Diperiksa dengan iOS 9, sepertinya buggy, saya menjawab dengan @ bryanchen, ia bekerja dengan baik !!
Akash Kava
3
Jawaban di bawah ini yang menggunakan IMO URLComponentsdan URLQueryItemjauh lebih bersih.
Aaron Brager
65

Anda dapat menggunakan komponen URLC untuk menghindari keharusan secara manual menyandikan string kueri Anda:

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
}
Leo Dabus
sumber
7
Jawaban ini perlu lebih diperhatikan, karena ada masalah dengan semua yang lain (meskipun untuk bersikap adil mereka mungkin merupakan praktik terbaik pada saat itu).
Asa
4
Sayangnya, URLQueryItemtidak selalu disandikan dengan benar. Misalnya, Formula+Oneakan dikodekan ke Formula+One, yang akan diterjemahkan ke Formula One. Karena itu berhati-hatilah dengan tanda plus.
Sulthan
37

Swift 3:

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

1. encodingQuery:

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

hasil:

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

hasil:

"http:%2F%2Fwww.ihtc.cc%3Fname=htc&title=iOS%E5%BC%80%E5%8F%91%E5%B7%A5%E7%A8%8B%E5%B8%88"
iHTCboy
sumber
Saya menggunakan solusi pertama tetapi saya ingin teks saya kembali, seperti iOS 开发 工程师.
Akshay Phulare
2
Menggunakan urlHostAlloweduntuk menyandikan parameter kueri salah karena tidak akan menyandikan ?, =dan +. Saat menyandikan parameter kueri, Anda harus menyandikan nama dan nilai parameter secara terpisah dan benar. Ini tidak akan berfungsi dalam kasus umum.
Sulthan
@Sulthan .. Apakah Anda menemukan solusi / alternatif untukurlHostAllowed
Bharath
@Harhar Ya, Anda harus membangun karakter yang Anda tentukan sendiri, misalnya stackoverflow.com/a/39767927/669586 atau cukup gunakan URLComponents.
Sulthan
Komponen URLC juga tidak mengkodekan +char. Jadi satu-satunya pilihan adalah melakukannya secara manual: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
}
Hong Wei
sumber
2
Anda perlu memasukkan `` (spasi) dalam rangkaian karakter
AJP
1
Anda juga perlu menyertakan ^
Mani
26

Cepat 4

Untuk menyandikan parameter dalam URL saya menemukan menggunakan .alphanumericskarakter mengatur opsi termudah:

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

Menggunakan Set Karakter standar untuk Pengkodean URL (suka URLQueryAllowedCharacterSetatau URLHostAllowedCharacterSet) tidak akan berfungsi, karena mereka tidak mengecualikan =atau &karakter.

Catatan bahwa dengan menggunakan .alphanumericsitu akan mengkodekan beberapa karakter yang tidak perlu dikodekan (seperti -, ., _atau ~- lihat 2.3 karakter Unreserved. Dalam RFC 3986). Saya menemukan menggunakan .alphanumericslebih sederhana daripada membangun set karakter khusus dan tidak keberatan beberapa karakter tambahan untuk dikodekan. Jika itu mengganggu Anda, buat set karakter khusus seperti yang dijelaskan dalam Cara persentase menyandikan String URL , seperti misalnya:

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

Peringatan: The encodedparameter kekuatan yang terbuka. Untuk string unicode yang tidak valid, mungkin macet. Lihat Mengapa nilai pengembalian String.addingPercentEncoding () opsional? . Alih-alih membuka paksa encoded!Anda dapat menggunakan encoded ?? ""atau menggunakan if let encoded = ....

Marián Černý
sumber
1
.aphanumerics berhasil, terima kasih! Semua set karakter lainnya tidak lepas dari & yang menyebabkan masalah saat menggunakan string sebagai parameter.
Dion
14

Semuanya sama

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

// test%2Ftest
Bryan Chen
sumber
Anda tidak .bridgeToOvjectiveC()mendebat kedua dan tidak mendapatkan "Tidak dapat mengonversi tipe ekspresi 'CFString!' untuk mengetik 'CFString!' "?
Kreiri
@ Kriri Mengapa diperlukan? Baik taman bermain dan REPL senang dengan kode saya.
Bryan Chen
Punyaku bukan: / (beta 2)
Kreiri
1
Ini adalah jawaban yang lebih baik karena menyandikan & dengan benar.
Sam
13

Swift 4:

Itu tergantung oleh aturan pengkodean yang diikuti oleh server Anda.

Apple menawarkan metode kelas ini, tetapi tidak melaporkan jenis protokol RCF yang diikuti.

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

Dengan mengikuti alat yang bermanfaat ini, Anda harus menjamin penyandian karakter ini untuk parameter Anda:

  • $ (Tanda Dolar) menjadi% 24
  • & (Ampersand) menjadi% 26
  • + (Plus) menjadi% 2B
  • , (Koma) menjadi% 2C
  • : (Colon) menjadi% 3A
  • ; (Semi-Colon) menjadi% 3B
  • = (Setara) menjadi% 3D
  • ? (Tanda Tanya) menjadi% 3F
  • @ (Komersial A / At) menjadi% 40

Dengan kata lain, berbicara tentang pengkodean URL, Anda harus mengikuti protokol RFC 1738 .

Dan Swift tidak mencakup pengkodean dari + char misalnya , tetapi ia bekerja dengan baik dengan tiga @:? karakter.

Jadi, untuk mengkodekan dengan benar setiap parameter Anda, .urlHostAllowedopsi tidak cukup, Anda harus menambahkan juga karakter khusus seperti:

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

Semoga ini bisa membantu seseorang yang menjadi gila untuk mencari informasi ini.

Alessandro Ornano
sumber
Implementasi Anda sepenuhnya salah. Bagaimana parameter "věž" dikodekan?
Marián Černý
13

Swift 4 (tidak diuji - beri komentar apakah berfungsi atau tidak. Terima kasih @sumizome untuk saran)

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

Cepat 3

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

Swift 2.2 (Meminjam dari Zaph dan mengoreksi kunci kueri url dan nilai parameter)

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

Contoh:

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"

Ini adalah versi pendek dari jawaban Bryan Chen. Saya kira itu urlQueryAllowedmemungkinkan karakter kontrol yang baik-baik saja kecuali mereka membentuk bagian dari kunci atau nilai dalam string kueri Anda pada titik mana mereka perlu melarikan diri.

AJP
sumber
2
Saya suka solusi Swift 3, tetapi tidak berfungsi untuk saya di Swift 4: "Tidak dapat menggunakan anggota yang bermutasi pada nilai yang tidak dapat diubah: 'urlQueryAllowed' adalah properti dapatkan-saja".
Marián Černý
@ MariánČerný hanya membuat CharacterSet bisa berubah (dengan var) dan kemudian memanggilnya .removedi langkah kedua.
sumizome
Saya percaya ini dan sebagian besar solusi lainnya memiliki masalah ketika menerapkan metode dua kali, misalnya ketika memasukkan URL dengan params yang di-encode dalam parameter URL lain.
FD_
@ FD_ Anda tahu atau Anda hanya punya firasat? Bisakah Anda bereksperimen dengannya dan mempostingnya kembali? Akan baik untuk memasukkan info ini jika demikian. Terima kasih.
AJP
@AJP Saya baru saja menguji semua cuplikan Anda. Swift 3 dan 4 berfungsi dengan baik, tetapi yang untuk Swift 2.2 tidak dengan benar menyandikan% 20 sebagai% 2520.
FD_
7

Cepat 4.2

Solusi satu jalur yang cepat. Ganti originalStringdengan String yang ingin Anda encode.

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

Demo Arena Bermain Online

ajzbc
sumber
Ini berhasil berkat: Anda dapat memeriksa dan mencoba mendekode dan menyandikan hasil pada. urldecoder.org
Rakshitha Muranga Rodrigo
4

Membutuhkan ini sendiri, jadi saya menulis ekstensi String yang keduanya memungkinkan untuk string URLEncoding, serta tujuan akhir yang lebih umum, mengubah kamus parameter menjadi URL gaya URL "GET" Parameter:

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

Nikmati!

BadPirate
sumber
2
Ini tidak menyandikan tanda '&'. Menggunakan tanda '&' di parameter akan meningkatkan querystring
Sam
Ini salah, itu tidak menyandikan &atau =dalam parameter. Periksa solusi saya sebagai gantinya.
Marián Černý
2

Yang ini bekerja untuk saya.

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
}

Saya menemukan fungsi di atas dari tautan ini: http://useyourloaf.com/blog/how-to-percent-encode-a-url-string/ .

Gaurav Singla
sumber
1

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

jaskiratjd
sumber
0

SWIFT 4.2

Kadang-kadang ini terjadi hanya karena ada ruang di slug ATAU tidak adanya pengkodean URL untuk parameter yang melewati 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)

CATATAN: Jangan lupa untuk menjelajahinya bitmapRepresentation.

Vrushal Raut
sumber
0

Ini bekerja untuk saya di Swift 5 . Kasing penggunaan mengambil URL dari clipboard atau yang serupa yang mungkin sudah lolos karakter tetapi yang juga mengandung karakter Unicode yang dapat menyebabkan URLComponentsatau URL(string:)gagal.

Pertama, buat rangkaian karakter yang mencakup semua karakter URL-legal:

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

Selanjutnya, perluas Stringdengan metode untuk menyandikan 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
    }

}

Yang dapat diuji seperti:

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)

Nilai urldi akhir: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

Perhatikan bahwa keduanya %20dan +spasi dipertahankan, karakter Unicode dikodekan, %20di dalam aslinya urlTexttidak dikodekan ganda, dan jangkar (fragmen, atau #) tetap.

Sunting: Sekarang memeriksa validitas masing-masing komponen.

KartunChess
sumber
0

Untuk Swift 5 hingga endcode string

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

Cara Penggunaan ?

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

Tidak satu pun dari jawaban ini yang berhasil untuk saya. Aplikasi kami mogok ketika url berisi karakter non-Inggris.

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

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

Tergantung pada parameter dari apa yang Anda coba lakukan, Anda mungkin ingin membuat set karakter Anda sendiri. Di atas memungkinkan untuk karakter bahasa Inggris, dan-._~/?%$!:

Jenel Ejercito Myers
sumber