Swift - mã hóa URL


295

Nếu tôi mã hóa một chuỗi như thế này:

var escapedString = originalString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)

nó không thoát khỏi dấu gạch chéo /.

Tôi đã tìm kiếm và tìm thấy mã C Mục tiêu này:

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

Có cách nào dễ dàng hơn để mã hóa URL và nếu không, làm cách nào để tôi viết mã này trong Swift?

Câu trả lời:


613

Swift 3

Trong Swift 3 có addingPercentEncoding

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

Đầu ra:

kiểm tra% 2 lần thử

Swift 1

Trong iOS 7 trở lên có stringByAddingPercentEncodingWithAllowedCharacters

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

Đầu ra:

kiểm tra% 2 lần thử

Sau đây là các bộ ký tự hữu ích (đảo ngược):

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

Nếu bạn muốn một bộ ký tự khác được thoát, hãy tạo một bộ:
Ví dụ có thêm ký tự "=":

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

Đầu ra:

kiểm tra% 2Ftest% 3D42

Ví dụ để xác minh các ký tự ascii không có trong tập hợp:

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
Không ai khác hoàn toàn lúng túng khi biết đoạn mã này dài bao lâu? Ý tôi là tên phương thức đó đã là địa ngục từ lâu, thậm chí không cần chọn bộ ký tự được phép.
thatidiotguy

38
Không, tôi ủng hộ sự dễ hiểu hơn việc đặt tên ngắn gọn. Tự động hoàn thành đưa nỗi đau ra. stringByAddingPercentEncodingWithAllowedCharacters()để lại chút nghi ngờ về những gì nó làm. Nhận xét thú vị khi xem xét từ "lúng túng" là bao lâu.
zaph

1
chuỗiByAddingPercentEncodingWith ALLowedChar character (.URLhost ALLowedCharacterSet ()) Không mã hóa tất cả các ký tự đúng Câu trả lời của Bryan Chen là một giải pháp tốt hơn.
Julio Garcia

2
@zaph Tôi đã thêm vào &bộ ký tự URLQueryAllowedCharacterSetvà tôi đã mã hóa từng ký tự. Đã kiểm tra iOS 9, có vẻ như lỗi, tôi đã đi với câu trả lời của @ bryanchen, nó hoạt động tốt !!
Akash Kava

3
Câu trả lời dưới đây sử dụng URLComponentsURLQueryItemIMO sạch hơn nhiều.
Aaron Brager

65

Bạn có thể sử dụng URLComponents để tránh phải tự mã hóa phần trăm chuỗi truy vấn của mình:

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
Câu trả lời này cần được chú ý nhiều hơn, vì có vấn đề với tất cả những câu hỏi khác (mặc dù công bằng mà nói, chúng có thể được thực hành tốt nhất vào thời điểm đó).
Asa

4
Đáng buồn thay, URLQueryItemkhông phải lúc nào cũng mã hóa chính xác. Ví dụ, Formula+Onesẽ được mã hóa thành Formula+One, sẽ được giải mã thành Formula One. Do đó hãy thận trọng với dấu cộng.
Sulthan

37

Swift 3:

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

1. mã hóa:

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

kết quả:

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

2. mã hóa:

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

kết quả:

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

Tôi đã sử dụng giải pháp đầu tiên nhưng tôi muốn gửi lại văn bản của mình, như iOS.
Akshay Phulare

2
Sử dụng urlHostAllowedđể mã hóa các thông số truy vấn trong không chính xác bởi vì nó sẽ không mã hóa ?, =+. Khi mã hóa các tham số truy vấn, bạn phải mã hóa tên và giá trị tham số một cách riêng biệt và chính xác. Điều này sẽ không làm việc trong một trường hợp chung.
Sulthan

@Sulthan .. Bạn có tìm thấy giải pháp / giải pháp thay thế nào chourlHostAllowed
Bharath

@Bharath Có, bạn phải tự xây dựng một nhân vật, ví dụ stackoverflow.com/a/39767927/669586 hoặc chỉ sử dụng URLComponents.
Sulthan

URLComponents không mã hóa +char quá. Vì vậy, lựa chọn duy nhất là thực hiện thủ công: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
Bạn cần bao gồm `` (dấu cách) trong chuỗi ký tự
AJP

1
Bạn cũng cần bao gồm ^
Mani

26

Swift 4

Để mã hóa một tham số trong URL tôi thấy bằng cách sử dụng .alphanumericský tự đặt tùy chọn dễ nhất:

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

Sử dụng bất kỳ Bộ ký tự chuẩn nào cho Mã hóa URL (như URLQueryAllowedCharacterSethoặc URLHostAllowedCharacterSet) sẽ không hoạt động, vì chúng không loại trừ =hoặc &ký tự.

Lưu ý rằng bằng cách sử dụng .alphanumericsnó sẽ mã hóa một số nhân vật mà không cần phải được mã hóa (như -, ., _hoặc ~- xem 2.3 nhân vật Không Giới Hạn. Trong RFC 3986). Tôi thấy việc sử dụng .alphanumericsđơn giản hơn là xây dựng một bộ ký tự tùy chỉnh và không bận tâm đến một số ký tự bổ sung được mã hóa. Nếu điều đó làm phiền bạn, hãy xây dựng một bộ ký tự tùy chỉnh như được mô tả trong Cách phần trăm mã hóa Chuỗi URL , ví dụ như:

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

Cảnh báo: Các encodedtham số là lực lượng nào. Đối với chuỗi unicode không hợp lệ, nó có thể bị sập. Xem tại sao giá trị trả về của String.addingPercentEncoding () là tùy chọn? . Thay vì ép buộc encoded!bạn có thể sử dụng encoded ?? ""hoặc sử dụng if let encoded = ....


1
.aphanumerics đã lừa, cảm ơn bạn! Tất cả các bộ ký tự khác không thoát khỏi & gây ra sự cố khi sử dụng chuỗi làm tham số.
Dion

14

Mọi thứ đều giống nhau

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

// test%2Ftest

Bạn không .bridgeToOvjectiveC()tranh luận lần thứ hai và không nhận được "Không thể chuyển đổi loại biểu thức 'CFString!' để nhập 'CFString!' "?
Kreiri

@Kreiri Tại sao cần thiết? Cả sân chơi và REPL đều hài lòng với mã của tôi.
Bryan Chen

Của tôi không: / (beta 2)
Kreiri

1
Đây là một câu trả lời tốt hơn vì nó mã hóa & chính xác.
Sam

13

Swift 4:

Nó phụ thuộc bởi các quy tắc mã hóa theo sau bởi máy chủ của bạn.

Apple cung cấp phương thức lớp này, nhưng nó không báo cáo loại giao thức RCF nào mà nó tuân theo.

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

Theo công cụ hữu ích này, bạn nên đảm bảo mã hóa các ký tự này cho các tham số của mình:

  • $ (Ký hiệu đô la) trở thành% 24
  • & (Ampersand) trở thành% 26
  • + (Cộng) trở thành% 2B
  • , (Dấu phẩy) trở thành% 2C
  • : (Đại tràng) trở thành% 3A
  • ; (Bán đại tràng) trở thành% 3B
  • = (Bằng) trở thành% 3D
  • ? (Dấu hỏi) trở thành% 3F
  • @ (A / At thương mại) trở thành% 40

Nói cách khác, nói về mã hóa URL, bạn nên tuân theo giao thức RFC 1738 .

Và Swift không bao gồm mã hóa của + char chẳng hạn , nhưng nó hoạt động tốt với ba @ :? ký tự

Vì vậy, để mã hóa chính xác từng tham số của bạn, .urlHostAllowedtùy chọn là không đủ, bạn cũng nên thêm các ký tự đặc biệt như ví dụ:

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

Hy vọng điều này sẽ giúp ai đó trở nên điên rồ để tìm kiếm những thông tin này.


Việc thực hiện của bạn là hoàn toàn sai. Làm thế nào một tham số "věž" được mã hóa?
Marián Černý

13

Swift 4 (chưa được thử nghiệm - vui lòng bình luận nếu nó hoạt động hay không. Cảm ơn @sumizome đã gợi ý)

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 (Mượn từ Zaph's và sửa lỗi cho các giá trị tham số và khóa truy vấn url)

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

Thí dụ:

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"

Đây là phiên bản ngắn hơn của câu trả lời của Bryan Chen. Tôi đoán rằng urlQueryAllowedviệc cho phép các ký tự điều khiển thông qua đó là tốt trừ khi chúng tạo thành một phần của khóa hoặc giá trị trong chuỗi truy vấn của bạn tại điểm mà chúng cần được thoát.


2
Tôi thích giải pháp Swift 3, nhưng nó không hoạt động với tôi trong Swift 4: "Không thể sử dụng thành viên đột biến trên giá trị bất biến: 'urlQuery ALLowed' là thuộc tính chỉ có được".
Marián Černý

@ MariánČerný chỉ cần làm cho các nhân vật biến đổi (với var) và sau đó gọi .removenó trong bước thứ hai.
sumizome

Tôi tin rằng điều này và hầu hết các giải pháp khác có vấn đề khi áp dụng phương pháp hai lần, ví dụ như khi bao gồm một URL có thông số được mã hóa trong các tham số của URL khác.
FD_

@FD_ bạn có biết hay bạn chỉ có linh cảm? Bạn có thể thử nghiệm với nó và gửi lại? Sẽ là tốt để bao gồm thông tin này nếu có. Cảm ơn bạn.
AJP

@AJP Tôi vừa thử tất cả các đoạn của bạn. Swift 3 và 4 hoạt động tốt, nhưng phiên bản dành cho Swift 2.2 không mã hóa chính xác% 20 dưới dạng% 2520.
FD_

7

Swift 4.2

Một giải pháp nhanh chóng một dòng. Thay thế originalStringbằng Chuỗi bạn muốn mã hóa.

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

Demo sân chơi trực tuyến


Điều này làm việc cảm ơn: bạn có thể kiểm tra và thử giải mã và mã hóa kết quả trên. urldecoder.org
Rakshitha Muranga Rodrigo

4

Bản thân tôi cũng cần điều này, vì vậy tôi đã viết một tiện ích mở rộng Chuỗi cho phép cả chuỗi URLEncoding, cũng như mục tiêu kết thúc phổ biến hơn, chuyển đổi từ điển tham số thành Tham số URL kiểu "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
    }
}

Thưởng thức!


2
Điều này không mã hóa dấu '&'. Sử dụng '&' trong một tham số sẽ giúp tăng chuỗi truy vấn
Sam

Điều này là sai, nó không mã hóa &hoặc =trong các tham số. Kiểm tra giải pháp của tôi thay thế.
Marián Černý

2

Đây là một trong những làm việc cho tôi.

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
}

Tôi tìm thấy chức năng trên từ liên kết này: http://useyourloaf.com/blog/how-to-percent-encode-a-url-opes/ .


1

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


0

Chuyển đổi 4.2

Đôi khi điều này xảy ra chỉ vì có khoảng trống trong sên HOẶC không có mã hóa URL cho các tham số truyền qua 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)

LƯU Ý: Đừng quên khám phá về bitmapRepresentation.


0

Điều này làm việc cho tôi trong Swift 5 . Trường hợp sử dụng đang lấy một URL từ bảng ghi tạm hoặc tương tự có thể đã thoát các ký tự nhưng cũng chứa các ký tự Unicode có thể gây ra URLComponentshoặc URL(string:)thất bại.

Đầu tiên, tạo một bộ ký tự bao gồm tất cả các ký tự hợp pháp 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
    }
}

Tiếp theo, mở rộng Stringbằng một phương pháp để mã hóa 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
    }

}

Mà có thể được kiểm tra như:

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)

Giá trị urlở cuối: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

Lưu ý rằng cả hai %20+khoảng cách đều được giữ nguyên, các ký tự Unicode được mã hóa, %20trong bản gốc urlTextkhông được mã hóa kép và phần neo (đoạn hoặc #) vẫn còn.

Chỉnh sửa: Bây giờ kiểm tra tính hợp lệ của từng thành phần.


0

Đối với chuỗi Swift 5 đến mã cuối

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

Sử dụng như thế nào?

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

0

Không có câu trả lời nào trong số này làm việc cho tôi. Ứng dụng của chúng tôi đã bị sập khi một url chứa các ký tự không phải tiếng Anh.

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

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

Tùy thuộc vào các tham số của những gì bạn đang cố gắng làm, bạn có thể muốn tạo bộ ký tự của riêng bạn. Ở trên cho phép các ký tự tiếng Anh, và-._~/?%$!:

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.