在WEB前端开发,服务器后台开发,或者是客户端开发中,对URL进行编码是一件很常见的事情,可是因为各个年代的RFC文档中的内容一直在变化,一些年代久远的代码就对URL编码和解码的规则和如今的有一些区别。前端
在1994年订制的RFC1738文档中,对字符串中的除了- _ .
以外的全部非字母数字字符都替换成百分号(%)后跟两位十六进制数,十六进制数中字母必须为大写。objective-c
在2005年定义的RFC3986中,将针对- _.~
四个字符以外的全部非字母数字字符进行百分号编码。固然 根据URL的类型不一样,有也一部分预留字符不须要进行编码,例如查询的URL
中能够包含? /
字符,不须要转义。更详细文档的能够查看RFC 3986。swift
addingPercentEncoding(withAllowedCharacters:
是iOS7以后出现的新API用于url encode
。服务器
全部类型的URL中,"-_.~"
都不该该被转码ui
var str = "-_.~" var encodeStr = str.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) print(encodeStr ?? "") // -_.~
var str = "#" var encodeStr = str.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) print(encodeStr ?? "") // %23
CharacterSet
是一个结构体,CharacterSet.urlHostAllowed
等预制类型包含了全部不须要被转码的字符,反过来讲就是指明了须要被转码的字符。CharacterSet
类中提供了一些经常使用的URL转码的类型:编码
* CharacterSet.urlHostAllowed: 被转义的字符有 "#%/<>?@\^`\{\|\}
* CharacterSet.urlPathAllowed: 被转义的字符有 "#%;<>?[\]^`\{\|\}
* CharacterSet.urlUserAllowed: 被转义的字符有 #%/<>?@\^`\{\|\}
* CharacterSet.urlQueryAllowed: 被转义的字符有 "#%<>[\]^`\{\|\}
* CharacterSet.urlPasswordAllowed 被转义的字符有 "#%/:<>?@[\]^`\{\|\}
为何说CharacterSet.urlHostAllowed
包含的是全部不须要被转码的字符,能够用两句代码验证:url
let unicode = "1".unicodeScalars.flatMap{ $0 }[0] print(CharacterSet.urlHostAllowed.contains(unicode)) //输出TRUE
因此,自定义转码字符的集合应该取反字符集:spa
let str2 = "#/%/<>?@" let custom = CharacterSet(charactersIn: "#").inverted let result = str2.addingPercentEncoding(withAllowedCharacters: custom) ?? "" print(result) //输出 %23/%/<>?@
能够看到只有#
被编码。在一些特殊的需求中会用到自定义编码集合,例如BASE64转码后的URL编码。code
API调用都是同样的,不过网上流传的比较多的是用的C API
token
NSString *ciphertext = @"saf#*&"; NSCharacterSet *set = [[NSCharacterSet characterSetWithCharactersInString:@"!*'();:@&=+$,/?%#[]"] invertedSet]; NSString *resultString = [ciphertext stringByAddingPercentEncodingWithAllowedCharacters: set];
C API
NSString *ciphertext = @"saf#*&"; NSString *encodedStr = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes (kCFAllocatorDefault, (CFStringRef)ciphertext, NULL, CFSTR("!*'();:@&=+$,/?%#[]"), kCFStringEncodingUTF8));