前言
在WEB前端開發,服務器后台開發,或者是客戶端開發中,對URL進行編碼是一件很常見的事情,但是由於各個年代的RFC文檔中的內容一直在變化,一些年代久遠的代碼就對URL編碼和解碼的規則和現在的有一些區別。
在1994年訂制的RFC1738文檔中,對字符串中的除了- _ .
之外的所有非字母數字字符都替換成百分號(%)后跟兩位十六進制數,十六進制數中字母必須為大寫。
在2005年定義的RFC3986中,將針對- _.~
四個字符之外的所有非字母數字字符進行百分號編碼。當然 根據URL的類型不同,有也一部分預留字符不需要進行編碼,例如查詢的URL
中可以包含? /
字符,不需要轉義。更詳細文檔的可以查看RFC 3986。
Swift url encode
addingPercentEncoding(withAllowedCharacters:
是iOS7之后出現的新API用於url encode
。
標准轉碼
所有類型的URL中,"-_.~"
都不應該被轉碼
var str = "-_.~" var encodeStr = str.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) print(encodeStr ?? "") // -_.~
var str = "#" var encodeStr = str.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) print(encodeStr ?? "") // %23
CharacterSet
CharacterSet
是一個結構體,CharacterSet.urlHostAllowed
等預制類型包含了所有不需要被轉碼的字符,反過來說就是指明了需要被轉碼的字符。CharacterSet
類中提供了一些常用的URL轉碼的類型:
* CharacterSet.urlHostAllowed: 被轉義的字符有 "#%/<>?@\^`\{\|\}
* CharacterSet.urlPathAllowed: 被轉義的字符有 "#%;<>?[\]^`\{\|\}
* CharacterSet.urlUserAllowed: 被轉義的字符有 #%/<>?@\^`\{\|\}
* CharacterSet.urlQueryAllowed: 被轉義的字符有 "#%<>[\]^`\{\|\}
* CharacterSet.urlPasswordAllowed 被轉義的字符有 "#%/:<>?@[\]^`\{\|\}
為什么說CharacterSet.urlHostAllowed
包含的是所有不需要被轉碼的字符,可以用兩句代碼驗證:
let unicode = "1".unicodeScalars.flatMap{ $0 }[0] print(CharacterSet.urlHostAllowed.contains(unicode)) //輸出TRUE
所以,自定義轉碼字符的集合應該取反字符集:
let str2 = "#/%/<>?@" let custom = CharacterSet(charactersIn: "#").inverted let result = str2.addingPercentEncoding(withAllowedCharacters: custom) ?? "" print(result) //輸出 %23/%/<>?@
可以看到只有#
被編碼。在一些特殊的需求中會用到自定義編碼集合,例如BASE64轉碼后的URL編碼。
Objective-C url encode
API調用都是一樣的,不過網上流傳的比較多的是用的C API
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));