一.URL编码
URL串只能包含限制性的少数字符。根据RFC2396的定义,允许在URL中出现的字符分为保留字符和非保留字符。
- 保留字符:
reserved = “;” | “/” | “?” | “:” | “@” | “&” | “=” | “+” | “$” | “,”
保留字符主要用于分隔URL的各个部分。 - 非保留字符:
unreserved = alphanum | mark
mark = “-” | “_” | “.” | “!” | “~” | “*” | “‘” | “(” | “)”
非保留字符包含大小写的英文字母,数字,以及mark中的符号。
除这些字符以外的其他字符如果要出现在URL中,需要进行编码,编码的格式为escaped = “%” hex hex,即“%”号后接两个16进制数字,编码中的数字为该字符的二进制表示。例如空格编码后的表示为%20,”~”编码后的表示为%7E,中文“相”的编码表示为%E7%9B%B8。
保留的字符用于分隔URL组件时不需编码,当其本身为数据,即为组件的一部分时也需要进行编码,同样”%”本身作为数据时也需要进行编码。
RFC3986对URL语法进行了更新,对URL中可以出现的字符也进行了更新:
- 保留字符:
reserved = gen-delims / sub-delims
gen-delims = “:” / “/” / “?” / “#” / “[" / "]” / “@”
sub-delims = “!” / “$” / “&” / “‘” / “(” / “)” / “*” / “+” / “,” / “;” / “=” - 非保留字符:
unreserved = alphanum | mark
mark = “-” | “_” | “.” | “!” | “~” | “*” | “‘” | “(” | “)”
与RFC2396相比,非保留字符没有变化,保留字符分为了两类gen-delims,sub-delims,其中增加了三个保留字符”#” / “[" / "]“。
gen-delims用于分隔各个组件,sub-delims用于分隔组件内部的数据。例如http://www.winddisk.com/wp-admin/post.php?post=999&action=edit。gen-delims中的”:”分隔协议与主机,”/”分隔主机与路径,“?”分隔主机和查询参数。sub-delims中的”&”用于分隔查询参数中的键值对,”=”用于分隔键值对中的键和值。
二.NSString URL编码方法
NSString的stringByAddingPercentEscapesUsingEncoding:方法可将普通字符串进行编码生成URL字符串,其会将所有不允许出现的URL中的字符进行转义编码。经测试,其根据的是RFC2396协议,因为其对RFC3986新增的保留字符”#” / “[" / "]“也进行了编码。
NSString* RFC3986String = @"azAZ09-._~:/?#[]@!$&'()*+,;=";
NSString* RFC2396String = @"azAZ09-_.!~*'();/?:@&=+$,";
NSString* RFC3986Url = [RFC3986String stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString* RFC2396Url = [RFC2396String stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSLog(@"RFC3986Str:%@",RFC3986String);
NSLog(@"RFC3986Url:%@",RFC3986Url);
NSLog(@"RFC2396Str:%@",RFC2396String);
NSLog(@"FFC2396Url:%@",RFC2396Url);
其输出结果为
RFC3986Str: azAZ09-._~:/?#[]@!$&'()*+,;=
RFC3986Url: azAZ09-._~:/?%23%5B%5D@!$&'()*+,;=
RFC2396Str: azAZ09-_.!~*'();/?:@&=+$,
FFC2396Url: azAZ09-_.!~*'();/?:@&=+$,
可见,对字符”#” / “[" / "]“也进行了编码。
stringByAddingPercentEscapesUsingEncoding 对应的解码方法为stringByReplacingPercentEscapesUsingEncoding:
三.CFURL 编码方法
为了更方便的控制对哪些字符编码,哪些字符不编码,可以使用CFURL的CFURLCreateStringByAddingPercentEscapes方法,
其定义为
CFStringRef CFURLCreateStringByAddingPercentEscapes (
CFAllocatorRef allocator,
CFStringRef originalString,
CFStringRef charactersToLeaveUnescaped,
CFStringRef legalURLCharactersToBeEscaped,
CFStringEncoding encoding
);
- allocator为新字符串内存分配方式,可传NULL或kCFAllocatorDefault;
- originalString为原始需编码字符串;
- charactersToLeaveUnescaped为本身为非法URL字符但不需进行编码的字符串;
- legalURLCharactersToBeEscaped为本身为合法URL字符但需要进行编码的字符串。
那么,最后需要编码的字符为 RFC2396中定义的非法URL字符 + legalURLCharactersToBeEscaped – charactersToLeaveUnescaped.
注:苹果文档中说依据的时RFC3986,但测试发现和stringByAddingPercentEscapesUsingEncoding一样,为RFC2396.Why?
其对应的解码方法为CFURLCreateStringByReplacingPercentEscapes
CFStringRef CFURLCreateStringByReplacingPercentEscapes (
CFAllocatorRef allocator,
CFStringRef originalString,
CFStringRef charactersToLeaveEscaped
);
示例代码:
#import "NSString+URLEncoding.h"
@implementation NSString (OAURLEncodingAdditions)
- (NSString *)URLEncodedString
{
NSString *result = (NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
(CFStringRef)self, NULL,CFSTR("!*'()^;:@&=+$,/?%#[]"),kCFStringEncodingUTF8);
[result autorelease];
return result;
}
- (NSString*)URLDecodedString
{
NSString *result = (NSString *)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault,
(CFStringRef)self, CFSTR(""), kCFStringEncodingUTF8);
[result autorelease];
return result;
}
@end
参考:
RFC2396 – Uniform Resource Identifiers (URI): Generic Syntax
RFC3986 – Uniform Resource Identifier (URI): Generic Syntax
NSString Class Reference
CFURL Reference
详解Javascript中的Url编码/解码
A Better URL Encoding Method
Objective-C 对 URL 进行 URLEncode 编码
URL Encoding