iOS URL 编码
2017-01-08 01:26 v2m 阅读(773) 评论(0) 编辑 收藏 举报一、iOS 中的NSURL编码
iOS 中,NSURL 的基本样式是
scheme://username:password@host:port/path?query#fragment
RFC 1738规定:
Thus, only alphanumerics, the special characters "$-_.+!*'(),", and
reserved characters used for their reserved purposes may be used
unencoded within a URL.
On the other hand, characters that are not required to be encoded
(including alphanumerics) may be encoded within the scheme-specific
part of a URL, as long as they are not being used for a reserved
purpose.
所以对一些不符合规定的字符我们需要进行编码。就是“URL编码”,也叫“百分号编码”。一般都是把不符合的字符转成UTF-8编码,然后每两位(2 BYTE)前面加上‘%’。
在以前,我们经常使用下面的函数去排除 @"!*'();😡&=+$,/?%#[]" 这些字符,
- (nullable NSString *)stringByAddingPercentEscapesUsingEncoding:(NSStringEncoding)enc;
CF_EXPORT CFStringRef CFURLCreateStringByAddingPercentEscapes(CFAllocatorRef allocator, CFStringRef originalString, CFStringRef charactersToLeaveUnescaped, CFStringRef legalURLCharactersToBeEscaped, CFStringEncoding encoding);
现在这两个函数已经 deprecate 了,建议使用新的方法
- (nullable NSString *)stringByAddingPercentEncodingWithAllowedCharacters:(NSCharacterSet *)allowedCharacters NS_AVAILABLE(10_9, 7_0);
文档上说这个函数默认使用 UTF-8 编码;而且可以根据特定的url 部分,使用不同的NSCharacter(如果字符虽然在allowedCharacters里面但是超过了 7-bit ASCII 范围,也会转码)。
上面的不同字符集指的是:
@property (class, readonly, copy) NSCharacterSet *URLUserAllowedCharacterSet NS_AVAILABLE(10_9, 7_0);
@property (class, readonly, copy) NSCharacterSet *URLPasswordAllowedCharacterSet NS_AVAILABLE(10_9, 7_0);
@property (class, readonly, copy) NSCharacterSet *URLHostAllowedCharacterSet NS_AVAILABLE(10_9, 7_0);
@property (class, readonly, copy) NSCharacterSet *URLPathAllowedCharacterSet NS_AVAILABLE(10_9, 7_0);
@property (class, readonly, copy) NSCharacterSet *URLQueryAllowedCharacterSet NS_AVAILABLE(10_9, 7_0);
@property (class, readonly, copy) NSCharacterSet *URLFragmentAllowedCharacterSet NS_AVAILABLE(10_9, 7_0);
二、NSURL 相关 NSCharacterSet
一个疑问是上面的 CharacterSet 到底包含了哪些字符,思路就是根据下面两个函数
- (BOOL)hasMemberInPlane:(uint8_t)thePlane;
- (BOOL)longCharacterIsMember:(UTF32Char)theLongChar;
先判断 set 是不是有 plane 里面的字符,然后再遍历 plane 里面的字符是不是在 set 里面(参考5)。最后得到了如下结果
URLUserAllowedCharacterSet
[!$&\'()*+,-.0123456789;=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~]
URLPasswordAllowedCharacterSet
[!$&\'()*+,-.0123456789;=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~]
URLHostAllowedCharacterSet
[!$&\'()*+,-.0123456789:;=ABCDEFGHIJKLMNOPQRSTUVWXYZ[]_abcdefghijklmnopqrstuvwxyz~]
URLPathAllowedCharacterSet
[!$&\'()*+,-./0123456789:=@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~]
URLQueryAllowedCharacterSet
[!$&\'()*+,-./0123456789:;=?@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~]
URLFragmentAllowedCharacterSet
[!$&\'()*+,-./0123456789:;=?@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~]
参考6中对于 plane 的说明如下:
$$
目前的Unicode字符分为17组编排,每组称为平面(Plane),而每平面拥有65536(即2^{16})个代码点。然而目前只用了少数平面。
$$
其中整型转Unicode字符,用了下面的函数
UTF32Char c1 = OSSwapHostToLittleInt32(c); // To make it byte-order safe
NSString *s = [[NSString alloc] initWithBytes:&c1 length:4 encoding:NSUTF32LittleEndianStringEncoding];
其中 OSSwapHostToLittleInt32 定义在 <libkern/OSByteOrder.h> 中,用 NSSwapHostLongToLittle 这个函数也可以。
let uniChar = UnicodeScalar(unicode)
其实这里可以看到是有问题的,比如一个query 是 a==b,这个是不会被编码的,然而怎么断句就有问题了,所以最好还是要结合一下自己写的 Custom Character Set。
三、一些说明
- RFC 3986(参考7)的一些定义
保留字符= gen-delims / sub-delims
gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
/ "*" / "+" / "," / ";" / "="
允许的字符= ALPHA / DIGIT / "-" / "." / "_" / "~"
- RFC 1738(参考1)的一些定义
不安全字符:
空格(SP) "<" and ">" """ "#" "%" "{", "}", "|", "", "^", "~",
"[", "]" "`"
这些都应该始终被encode的
保留字符:
scheme : ";", "/", "?", ":", "@", "=" and "&"
通常一个字节以字符形式或者编码形式呈现意思是一样的。
但是对scheme的保留字符编码会改变URL的语义。
所以只有 apphanumerics,特殊字符"$-_.+!*'(),",以及用在
保留目的地的保留字符或许不应该去编码。
另一方面,不需要编码的字符(包括字母数字),
只要它们不用于保留目的可以在URL 的 scheme 内编码。
- 被废弃的
CFURLCreateStringByAddingPercentEscapes
已经排除了 RFC 2396 中不合法的字符,所以最开始的那个 "!*'();😡&=+$,/?%#[]" 也是有问题的。
参考:
- http://www.ietf.org/rfc/rfc1738.txt
- https://zh.wikipedia.org/wiki/统一资源定位符
- https://zh.wikipedia.org/wiki/百分号编码
- http://stackoverflow.com/questions/32064754/how-to-use-stringbyaddingpercentencodingwithallowedcharacters-for-a-url-in-swi
- http://stackoverflow.com/questions/15741631/nsarray-from-nscharacterset
- https://zh.wikipedia.org/wiki/Unicode字符平面映射
- https://tools.ietf.org/html/rfc3986