如何在Swift中编码URL

155

这是我的URL

问题在于,address字段没有被附加到urlpath上。

有人知道为什么吗?

var address:string
address = "American Tourister, Abids Road, Bogulkunta, Hyderabad, Andhra Pradesh, India"
let urlpath = NSString(format: "http://maps.googleapis.com/maps/api/geocode/json?address="+"\(address)")

2
我想这个代码 let urlpath = "http://maps.googleapis.com/maps/api/geocode/json?address=\(address)") 已经足够了。 - jithinroy
2
"Some string" + "\(aString)" 是过度设计了... 只需使用 "Some string \(aString)" 即可。 - nf071590
2
或者只是 "some string" + aString。 - Gusutafu
我们可以使用 stringByAddingPercentEncodingWithAllowedCharacters 方法。你可以在这个回答中找到更多细节: https://dev59.com/uVwY5IYBdhLWcg3wwqH1#44643893 - Lal Krishna
13个回答

261

Swift 4.2

var urlString = originalString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)

Swift 3.0

var address = "American Tourister, Abids Road, Bogulkunta, Hyderabad, Andhra Pradesh, India"
let escapedAddress = address.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
let urlpath = String(format: "http://maps.googleapis.com/maps/api/geocode/json?address=\(escapedAddress)")

使用stringByAddingPercentEncodingWithAllowedCharacters方法:

var escapedAddress = address.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())

使用stringByAddingPercentEscapesUsingEncoding: iOS 9和OS X v10.11中已过时

var address = "American Tourister, Abids Road, Bogulkunta, Hyderabad, Andhra Pradesh, India"
var escapedAddress = address.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
let urlpath = NSString(format: "http://maps.googleapis.com/maps/api/geocode/json?address=\(escapedAddress)")

13
你可能希望修改字符集,这样如果address的值中出现+&,它们将被转义成百分号编码。不幸的是,URLQueryAllowedCharacterSet会让它们未经转义而通过,这将改变服务器解析的数值。 - Rob
8
Rob是正确的,var escapedAddress = address.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet()) 对于 &+ 字符仍然是无效的。 - pasevin
5
stringByAddingPercentEscapesUsingEncoding: 在iOS 9中已被弃用,建议使用stringByAddingPercentEncodingWithAllowedCharacters方法。 - webo80
5
让 s 等于可变的 NSCharacterSet.URLQueryAllowedCharacterSet()。然后,将“+”和“&”添加到 s 中。 - Bryan Chen
2
urlString = urlString.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)! 该行代码用于对URL字符串进行编码,以确保其符合URL查询允许的字符集。 - Abdul Yasin
显示剩余7条评论

84
如果您将要添加到URL中的值可能包含保留字符(由RFC 3986第2节定义),则您可能需要重新调整百分号转义。值得注意的是,虽然&+是URL中有效的字符,但它们不在URL查询参数值内有效(因为&用作查询参数之间的分隔符,会过早终止您的值,而+会被转换为空格字符)。不幸的是,标准的百分号转义会使这些分隔符未转义。
因此,您可能希望对不在RFC 3986未保留字符列表中的所有字符进行百分号转义:

URI中允许但没有保留目的的字符称为未保留字符。这些包括大写字母、小写字母、十进制数字、连字符、句点、下划线和波浪号。

     未保留字符 = ALPHA / DIGIT /“-” /“.” /“_” /“~”
在第3.4节中,RFC进一步考虑将?/添加到查询中允许的字符列表中:

斜杠(“/”)和问号(“?”)可以表示查询组件中的数据。请注意,当它用作相对引用(第5.1节)的基本URI时,一些旧的错误实现可能无法正确处理此类数据,似乎是因为它们在查找分层分隔符时未能区分查询数据和路径数据。但是,由于查询组件通常用于携带以“键=值”对形式的标识信息,并且一个经常使用的值是对另一个URI的引用,因此有时为了可用性而避免百分数编码这些字符。

现在,通常使用URLComponents来百分数编码查询值:

var address = "American Tourister, Abids Road, Bogulkunta, Hyderabad, Andhra Pradesh, India"
var components = URLComponents(string: "http://maps.googleapis.com/maps/api/geocode/json")!
components.queryItems = [URLQueryItem(name: "address", value: address)]
let url = components.url!

顺便提一下,尽管在上述RFC中没有考虑到,但W3C HTML规范的5.2节,URL编码表单数据指出,application/x-www-form-urlencoded请求也应将空格字符替换为+字符(并包括不应被转义的字符中的星号)。不幸的是,URLComponents不能正确地进行百分比转义,因此苹果公司建议您在检索URLComponents对象的url属性之前手动进行百分比转义。
// configure `components` as shown above, and then:

components.percentEncodedQuery = components.percentEncodedQuery?.replacingOccurrences(of: "+", with: "%2B")
let url = components.url!

如果您需要Swift 2版本的翻译,其中我手动执行所有这些百分比转义,请参阅此答案的先前版本


9
这是更高级和更可靠的回应。 - pasevin
1
不,这就是我的观点。RFC3986和W3C HTML规范并不完全一致。W3C HTML规范明确指出:“如果字节在0x2A(*),0x2D(-),0x2E(.),0x30到0x39(0-9),0x41到0x5A(A-Z),0x5F(_),0x61到0x7A(a-z)范围内,则保留该字节,[否则百分号转义]。” W3C规范没有考虑 ~。实际上,这可能并不那么重要(大多数web服务器将接受未转义的 ~)。 - Rob
3
对于x-www-form-urlencoded部分给个赞。我看到其他所有答案都无法在这种情况下工作。 - Chadwick
这正是我需要在查询变量中发送 + 的方法。感谢您提供的文档。 - Keith Holliday
alphanumericCharacterSet 中有 103806 个字符(几乎涵盖了整个 Unicode),这可能会导致速度过慢。请查看我的答案,以获取更轻量级的方法。 - Desmond Hume
显示剩余4条评论

66

Swift 3:

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

3
非常棒的答案,谢谢。为了帮助搜索者,这个替换了旧的样式...**withAllowedCharacters: .urlQueryAllowed()**。 - Fattie
1
originalString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) 可以将字符串进行URL编码。 - Glenn Posadas
也为我工作吧。谢谢你!:D - Douglas Frari

35

Swift 2.0

let needsLove = "string needin some URL love"
let safeURL = needsLove.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!

编程技巧和窍门


16

URLQueryAllowedCharacterSet 不应该用于对查询参数进行 URL 编码,因为该字符集包含 &?/ 等在 URL 查询中作为分隔符的字符。

/?paramname=paramvalue&paramname=paramvalue

这些字符在URL查询作为整体时是允许的,但在参数值中不允许。

RFC 3986明确讨论了未保留字符,它们与允许的字符不同:

2.3. 未保留字符

URI中允许但没有保留目的的字符称为未保留字符。这包括大写字母、小写字母、十进制数字、连字符、句点、下划线和波浪号。

  unreserved  = ALPHA / DIGIT / "-" / "." / "_" / "~"
因此:
extension String {
    var URLEncoded:String {

        let unreservedChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~"
        let unreservedCharsSet: CharacterSet = CharacterSet(charactersIn: unreservedChars)
        let encodedString = self.addingPercentEncoding(withAllowedCharacters: unreservedCharsSet)!
        return encodedString
    }
}

上面的代码没有调用alphanumericCharacterSet,因为它返回的字符集非常大(103806个字符)。考虑到alphanumericCharacterSet允许多少Unicode字符,将其用于URL编码的目的将是错误的。

使用方法:

let URLEncodedString = myString.URLEncoded

1
不错的回答。但是你发了两次,最好将其中一个问题标记为重复。 - Eric Aya
1
我说错了吗?我甚至说“好答案”。 :/ 我的意思是不要发布相同的答案多次,仅此而已。 - Eric Aya
这个话题周围有很多错误的答案,我觉得更多的人在向代码中添加 bug 之前应该看到这个答案。不过好吧,现在我会专门回答这个问题了。 - Desmond Hume
这不是真的,你应该使用URLQueryAllowedCharacterSet来编码查询。但是不是你完全查询!你应该使用URLQueryAllowedCharacterSet对总查询中的每个值进行编码,这样它就可以工作了。 - rckoenes

8

Swift 4.1

根据所需选项(urlQueryAllowed)创建一个“字符集”。然后删除您不想要的其他字符(+&)。然后将该字符集传递给“addingPercentEncoding”。

var address = "American Tourister, Abids Road, Bogulkunta, Hyderabad, Andhra Pradesh, India"
var queryCharSet = NSCharacterSet.urlQueryAllowed
queryCharSet.remove(charactersIn: "+&")
let escapedAddress = address.addingPercentEncoding(withAllowedCharacters: queryCharSet)!
let urlpath = String(format: "http://maps.googleapis.com/maps/api/geocode/json?address=\(escapedAddress)")

这也是我最终做的。 我认为他们也需要一个urlQueryValueAllowed(尽管从技术上讲,“+”也被允许作为查询值的一部分)。 - Dylan Nicholson

6
XCODE 8,SWIFT 3.0
来源:grokswift
从字符串创建URL是一个容易出现错误的雷区。只要错过一个斜杠或者意外地对查询中的“?”进行URL编码,你的API调用就会失败,你的应用程序将无法显示任何数据(甚至在没有预料到这种可能性的情况下崩溃)。自从iOS 8以来,使用NSURLComponents和NSURLQueryItems构建URL的方法更好。
func createURLWithComponents() -> URL? {
    var urlComponents = URLComponents()
    urlComponents.scheme = "http"
    urlComponents.host = "maps.googleapis.com"
    urlComponents.path = "/maps/api/geocode/json"

    let addressQuery = URLQueryItem(name: "address", value: "American Tourister, Abids Road, Bogulkunta, Hyderabad, Andhra Pradesh, India")
    urlComponents.queryItems = [addressQuery]

    return urlComponents.url
}

以下是使用 guard 语句访问 URL 的代码。
guard let url = createURLWithComponents() else {
            print("invalid URL")
            return nil
      }
      print(url)

输出:

http://maps.googleapis.com/maps/api/geocode/json?address=American%20Tourister,%20Abids%20Road,%20Bogulkunta,%20Hyderabad,%20Andhra%20Pradesh,%20India

阅读更多:使用NSURLComponents和NSURLQueryItems构建URL

2
Swift 3.0 urlAsString = urlAsString.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)! Swift 3.0 urlAsString = urlAsString.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)! - Cristian Cardoso

5

补充Desmond Hume的答案,为String类扩展一个RFC 3986未保留字符有效编码函数(如果你需要编码查询FORM参数):

Swift 3

extension String {

    var RFC3986UnreservedEncoded:String {
        let unreservedChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~"
        let unreservedCharsSet: CharacterSet = CharacterSet(charactersIn: unreservedChars)
        let encodedString: String = self.addingPercentEncoding(withAllowedCharacters: unreservedCharsSet)!
        return encodedString
    }
}

4

更新至Swift 3:

var escapedAddress = address.addingPercentEncoding(
    withAllowedCharacters: CharacterSet.urlQueryAllowed)

3

在Mac OS 10.9 Mavericks和iOS 7中,引入了NSURLComponents类,它以一种非常方便的方式处理不同URL部分的编码。

NSURLComponents是一个基于RFC 3986设计的类,用于解析URL并从其组成部分构造URL。它的行为与符合旧版RFC的NSURL类略有不同。但是,您可以轻松地根据URL组件对象的内容获取NSURL对象,反之亦然。

let address = "American Tourister, Abids Road, Bogulkunta, Hyderabad, Andhra Pradesh, India"

let components = NSURLComponents(string: "http://maps.googleapis.com/maps/api/geocode/json")!
// create a query item key=value
let queryItem = NSURLQueryItem(name: "address", value: address)
// add the query item to the URL, NSURLComponents takes care of adding the question mark.
components.queryItems = [queryItem]
// get the properly percent encoded string
let urlpath = components.string!
print(urlpath)

这正是我需要的,让我的谷歌查询再次工作起来了!谢谢。 - Sethmr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接