Swift - 编码URL

Meg*_*kie 274 urlencode ios swift

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

var escapedString = originalString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
Run Code Online (Sandbox Code Playgroud)

它不会逃避斜线/.

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

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

是否有更简单的方法来编码URL,如果没有,我如何在Swift中编写它?

zap*_*aph 562

斯威夫特3

在Swift 3中有 addingPercentEncoding

let originalString = "test/test"
let escapedString = originalString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
print(escapedString!)
Run Code Online (Sandbox Code Playgroud)

输出:

测试%2Ftest

斯威夫特1

在iOS 7及以上版本中有 stringByAddingPercentEncodingWithAllowedCharacters

var originalString = "test/test"
var escapedString = originalString.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())
println("escapedString: \(escapedString)")
Run Code Online (Sandbox Code Playgroud)

输出:

测试%2Ftest

以下是有用的(反向)字符集:

URLFragmentAllowedCharacterSet  "#%<>[\]^`{|}
URLHostAllowedCharacterSet      "#%/<>?@\^`{|}
URLPasswordAllowedCharacterSet  "#%/:<>?@[\]^`{|}
URLPathAllowedCharacterSet      "#%;<>?[\]^`{|}
URLQueryAllowedCharacterSet     "#%<>[\]^`{|}
URLUserAllowedCharacterSet      "#%/:<>?@[\]^`
Run Code Online (Sandbox Code Playgroud)

如果要转义不同的字符集,请创建一个集:
添加了"="字符的示例:

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

输出:

测试%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)\'")
}
Run Code Online (Sandbox Code Playgroud)

  • 不,我赞成可以理解而不是简短的命名.自动完成功能可以解决问题.`stringByAddingPercentEncodingWithAllowedCharacters()`对它的作用毫无疑问.有趣的评论考虑了这个词有多长:"大吃一惊". (31认同)
  • 难道没有人对这段代码执行此操作的时间长度感到惊讶吗?我的意思是方法名称已经很长,即使没有选择允许的字符集. (6认同)
  • 使用`URLComponents`和`URLQueryItem`的下面的答案是更清洁的IMO. (3认同)
  • @zaph我将`&`添加到`URLQueryAllowedCharacterSet`的字符集中,我得到了每个字符的编码.用iOS 9检查,看起来像马车,我去了@bryanchen的答案,它运作良好! (2认同)

Leo*_*bus 52

您可以使用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"
}
Run Code Online (Sandbox Code Playgroud)
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
    }
}
Run Code Online (Sandbox Code Playgroud)
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
}
Run Code Online (Sandbox Code Playgroud)

  • 遗憾的是,`URLQueryItem` 并不总是正确编码。例如,`Formula+One` 将被编码为`Formula+One`,然后被解码为`Formula One`。因此,请谨慎使用加号。 (9认同)
  • 这个答案需要更多关注,因为所有其他问题都存在问题(尽管公平地认为它们当时可能是最佳实践). (6认同)

Hon*_*Wei 34

斯威夫特3:

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

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

  • 你需要在字符串中包含``(空格) (2认同)

iHT*_*boy 31

斯威夫特3:

let originalString = "http://www.ihtc.cc?name=htc&title=iOS?????"
Run Code Online (Sandbox Code Playgroud)

编码查询:

let escapedString = originalString.addingPercentEncoding(withAllowedCharacters:NSCharacterSet.urlQueryAllowed)
Run Code Online (Sandbox Code Playgroud)

结果:

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

2. encodingURL:

let escapedString = originalString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
Run Code Online (Sandbox Code Playgroud)

结果:

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

  • 使用 `urlHostAllowed` 编码查询参数不正确,因为它不会编码 `?`、`=` 和 `+`。在编码查询参数时,您必须分别正确地对参数名称和值进行编码。这在一般情况下不起作用。 (3认同)

Mar*_*rný 21

斯威夫特4

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

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

使用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!)"
Run Code Online (Sandbox Code Playgroud)

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

  • .aphannumerics 成功了,谢谢!所有其他字符集都没有转义 &amp;,这在使用字符串作为获取参数时会导致问题。 (2认同)

Bry*_*hen 14

一切都是一样的

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

// test%2Ftest
Run Code Online (Sandbox Code Playgroud)


Ale*_*ano 12

斯威夫特4:

它取决于您的服务器遵循的编码规则.

Apple提供此类方法,但它没有报告它遵循的RCF协议.

var escapedString = originalString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!
Run Code Online (Sandbox Code Playgroud)

在这个有用的工具之后,你应该保证为你的参数编码这些字符:

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

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

例如,Swift没有涵盖+ char的编码,但它适用于这三个@:?字符.

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

encodedParameter = parameter.replacingOccurrences(of: "+", with: "%2B")
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助那些疯狂的人搜索这些信息.


AJP*_*AJP 11

Swift 4(未经测试 - 请评论是否有效.感谢@sumizome提出建议)

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

斯威夫特3

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

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

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

例:

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"
Run Code Online (Sandbox Code Playgroud)

这是Bryan Chen的答案的缩短版.我猜这urlQueryAllowed是允许控制字符很好,除非它们构成查询字符串中的键或值的一部分,此时它们需要被转义.

  • 我喜欢Swift 3解决方案,但它在Swift 4中对我不起作用:"不能在不可变值上使用变异成员:'urlQueryAllowed'是一个只用属性". (2认同)

ajz*_*zbc 10

斯威夫特 4.2

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

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

在线游乐场演示


Car*_*ess 6

这在Swift 5中对我有用。用例是从剪贴板或类似的 URL 中获取 URL,该 URL 可能已经包含转义字符,但也包含 Unicode 字符,这可能会导致URLComponentsURL(string:)

\n\n

首先,创建一个包含所有 URL 合法字符的字符集:

\n\n
extension CharacterSet {\n\n    /// Characters valid in at least one part of a URL.\n    ///\n    /// 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.\n    static var urlAllowedCharacters: CharacterSet {\n        // Start by including hash, which isn\'t in any set\n        var characters = CharacterSet(charactersIn: "#")\n        // All URL-legal characters\n        characters.formUnion(.urlUserAllowed)\n        characters.formUnion(.urlPasswordAllowed)\n        characters.formUnion(.urlHostAllowed)\n        characters.formUnion(.urlPathAllowed)\n        characters.formUnion(.urlQueryAllowed)\n        characters.formUnion(.urlFragmentAllowed)\n\n        return characters\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

接下来,扩展String使用对 URL 进行编码的方法进行扩展:

\n\n
extension String {\n\n    /// Converts a string to a percent-encoded URL, including Unicode characters.\n    ///\n    /// - Returns: An encoded URL if all steps succeed, otherwise nil.\n    func encodedUrl() -> URL? {        \n        // Remove preexisting encoding,\n        guard let decodedString = self.removingPercentEncoding,\n            // encode any Unicode characters so URLComponents doesn\'t choke,\n            let unicodeEncodedString = decodedString.addingPercentEncoding(withAllowedCharacters: .urlAllowedCharacters),\n            // break into components to use proper encoding for each part,\n            let components = URLComponents(string: unicodeEncodedString),\n            // and reencode, to revert decoding while encoding missed characters.\n            let percentEncodedUrl = components.url else {\n            // Encoding failed\n            return nil\n        }\n\n        return percentEncodedUrl\n    }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

可以这样测试:

\n\n
let urlText = "https://www.example.com/\xed\x8f\xb4\xeb\x8d\x94/search?q=123&foo=bar&multi=eggs+and+ham&hangul=\xed\x95\x9c\xea\xb8\x80&spaced=lovely%20spam&illegal=<>#top"\nlet url = encodedUrl(from: urlText)\n
Run Code Online (Sandbox Code Playgroud)\n\n

的价值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

\n\n

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

\n\n

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

\n


Har*_*kar 5

对于 Swift 5 结束编码字符串

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

如何使用 ?

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