Swift-编码URL


295

如果我编码这样的字符串:

var escapedString = originalString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)

它不能逃脱斜线 /

我搜索并找到了此Objective C代码:

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

有没有更简单的方法来编码URL,否则,如何在Swift中编写此代码?

Answers:


613

迅捷3

在Swift 3中有 addingPercentEncoding

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

输出:

测试%2Ftest

斯威夫特1

在iOS 7及更高版本中, stringByAddingPercentEncodingWithAllowedCharacters

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

输出:

测试%2Ftest

以下是有用的(倒置)字符集:

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

如果要转义其他字符集,请创建一个字符集:
添加了“ =”字符的示例:

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

输出:

测试%2Ftest%3D42

验证不在集合中的ASCII字符的示例:

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
难道没有人完全为这段代码要花多长时间感到困惑吗?我的意思是,即使不选择允许的字符集,方法名称也已经很长了。
thatidiotguy 2014年

38
不,相对于简短的命名,我更倾向于理解性。自动完成功能消除了痛苦。stringByAddingPercentEncodingWithAllowedCharacters()对它的工作毫不怀疑。考虑到“吃惊的”一词有多长时间,这很有趣。
zaph 2014年

1
stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())无法正确编码所有字符Bryan Chen的答案是一个更好的解决方案。
Julio Garcia

2
@zaph我添加&到的字符集中, URLQueryAllowedCharacterSet并且我对每个字符进行了编码。使用iOS 9进行检查,看起来像是越野车,我接受了@bryanchen的回答,效果很好!
Akash Kava

3
下面的答案使用URLComponentsURLQueryItem更干净的IMO。
亚伦·布拉格

65

您可以使用URLComponents避免手动对查询字符串进行百分比编码:

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
这个答案需要更多的关注,因为所有其他答案都存在问题(尽管公平地说,它们在当时可能是最佳实践)。
Asa

4
可悲的是,URLQueryItem并非总是正确编码。例如,Formula+One将被编码为Formula+One,将被解码为Formula One。因此,请谨慎使用加号。
Sulthan '18

37

斯威夫特3:

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

1. encodingQuery:

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

结果:

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

结果:

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

我使用了第一个解决方案,但我想回复我的文字,例如iOS开发工程师。
Akshay Phulare '18年

2
使用urlHostAllowed中因为不正确的它不会编码编码查询参数?=+。对查询参数进行编码时,必须分别正确地对参数名称和值进行编码。在一般情况下,这是行不通的。
苏珊(Sulthan)

@Sulthan ..您是否找到任何解决方案/替代品urlHostAllowed
Bharath

@Bharath是的,您必须自己构建一个字符集,例如stackoverflow.com/a/39767927/669586或只使用URLComponents
苏珊(Sulthan)

URLComponents也不对+字符进行编码。因此,唯一的选择是手动进行:CharacterSet.urlQueryAllowed.subtracting(CharacterSet(charactersIn: "+"))
SoftDesigner,

36

斯威夫特3:

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

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

2
您需要在字符串中包含`(空格)
AJP

1
您还需要包括^
Mani

26

斯威夫特4

要在URL中编码参数,我发现使用.alphanumerics字符集是最简单的选项:

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

无法使用任何标准字符集进行URL编码(如URLQueryAllowedCharacterSetURLHostAllowedCharacterSet),因为它们不排除=&字符。

注意,通过使用.alphanumerics它编码无需编码的某些字符(如-._~-见2.3未保留的字符。在RFC 3986)。我发现使用的方法.alphanumerics比构造自定义字符集更简单,并且不介意要编码的其他一些字符。如果那让您感到困扰,请按照如何对URL字符串进行百分比编码中所述构造一个自定义字符集,例如:

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

警告:encoded参数已强制展开。对于无效的unicode字符串,它可能会崩溃。请参见为什么String.addingPercentEncoding()的返回值是可选的?。不用强制展开,encoded!您可以使用encoded ?? ""或使用if let encoded = ...


1
.aphanumerics做到了,谢谢!使用字符串作为get参数时,所有其他字符集都没有转义&,这会导致问题。
迪翁

14

一切都一样

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

// test%2Ftest

您没有.bridgeToOvjectiveC()第二个参数,也没有得到“无法转换表达式的类型'CFString!'。键入“ CFString!”?
Kreiri 2014年

@Kreiri为什么需要它?游乐场和REPL都对我的代码感到满意。
布赖恩·陈

我的不是:/(beta 2)
Kreiri 2014年

1
这是一个更好的答案,因为它正确编码了&。
山姆

13

斯威夫特4:

这取决于服务器遵循的编码规则。

Apple提供了此类方法,但没有报告遵循的那种RCF协议。

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

使用此有用的工具后,您应该保证为参数使用这些字符的编码:

  • $(美元符号)变为%24
  • &(&)变成%26
  • +(加号)变为%2B
  • ,(逗号)变为%2C
  • :(冒号)变为%3A
  • ; (分号)变为%3B
  • =(等于)变成%3D
  • ?(问号)变为%3F
  • @(商业A / At)变为%40

换句话说,谈到URL编码,您应该遵循RFC 1738协议

而且Swift不会覆盖+ char的编码,例如,它与这三个@:一起使用效果很好字符

因此,要正确编码每个参数,该.urlHostAllowed选项是不够的,还应添加特殊字符,例如:

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

希望这可以帮助某个变得疯狂的人搜索这些信息。


您的实现是完全错误的。参数“věž”将如何编码?
玛丽安·塞尔尼

13

Swift 4(未经测试-请评论它是否有效。感谢@sumizome的建议)

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

迅捷3

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

Swift 2.2(从Zaph借用并更正url查询键和参数值)

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

例:

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"

这是Bryan Chen答案的简短版本。我猜想这urlQueryAllowed将允许控制字符通过,除非它们构成查询字符串中键或值的一部分,否则必须将其转义。


2
我喜欢Swift 3解决方案,但在Swift 4中对我不起作用:“不能在不可变值上使用变异成员:'urlQueryAllowed'是仅获取属性”。
玛丽安·塞尔尼

@MariánČerný只是使CharacterSet可变(使用var),然后.remove在第二步中对其进行调用。
sumizome

我相信这种方法和大多数其他解决方案在两次应用该方法时都会遇到问题,例如,在另一个URL的参数中包含带有编码参数的URL时。
FD_

@FD_你知道还是只是预感?您可以试验一下并回发吗?如果可以的话,最好包含此信息。谢谢。
AJP

@AJP我刚刚测试了所有片段。Swift 3和4可以正常工作,但是Swift 2.2不能正确将%20编码为%2520。
FD_

7

斯威夫特4.2

一种快速的单线解决方案。替换originalString为要编码的字符串。

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

在线游乐场演示


这很有效:您可以检查并尝试对结果进行解码和编码。urldecoder.org
Rakshitha Muranga Rodrigo

4

自己需要这个,所以我写了一个String扩展名,它既允许URLEncoding字符串,也允许更常见的最终目标,将参数字典转换为“ GET”样式的URL参数:

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

请享用!


2
这不会编码“&”符号。在参数中使用“&”将使查询字符串变乱
Sam Sam

这是错误的,它没有编码&=在参数中。检查我的解决方案。
玛丽安·塞尔尼

2

这个正在为我工​​作。

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
}

我从此链接找到了上述功能:http : //useyourloaf.com/blog/how-to-percent-encode-a-url-string/


1

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


0

SWIFT 4.2

有时,发生这种情况的原因仅在于:在子标签中存在空间,或者缺少通过API URL传递参数的URL编码。

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

注意:不要忘记探索bitmapRepresentation


0

这在Swift 5中为我工作。该用例是从剪贴板或类似的网址中获取一个URL,该URL可能已经转义了字符,但还包含可能导致URLComponentsURL(string:)失败的Unicode字符。

首先,创建一个包含所有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
    }
}

接下来,扩展String一种用于编码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
    }

}

可以像这样测试:

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)

最后的值urlhttps://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

请注意,保留了%20+间距,都对Unicode字符进行了编码,%20原始字符中的urlText不会进行双重编码,并且锚点(片段或#)仍然保留。

编辑:现在检查每个组件的有效性。


0

对于Swift 5结束代码字符串

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

如何使用 ?

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

0

这些答案都不对我有用。网址包含非英语字符时,我们的应用程序崩溃了。

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

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

根据您要执行的操作的参数,您可能只想创建自己的字符集。以上允许使用英文字符,并且-._~/?%$!:

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.