我想获取这些特定字母的百分比编码字符串,如何在objective-c中执行此操作?
Reserved characters after percent-encoding
! * ' ( ) ; : @ & = + $ , / ? # [ ]
%21 %2A %27 %28 %29 %3B %3A %40 %26 %3D %2B %24 %2C %2F %3F %23 %5B %5D
请使用此字符串进行测试,看看它是否有效:
myURL = @"someurl/somecontent"
我希望字符串看起来像:
myEncodedURL = @"someurl%2Fsomecontent"
我已尝试使用stringByAddingPercentEscapesUsingEncoding: NSASCIIStringEncoding
但它不起作用,结果仍然与原始字符串相同。请指教。
答案 0 :(得分:141)
我发现stringByAddingPercentEscapesUsingEncoding:
和CFURLCreateStringByAddingPercentEscapes()
都不合适。 NSString
方法错过了很多字符,CF函数只允许您说出要转义的(特定)字符。正确的规范是逃避除小集之外的所有字符。
为了解决这个问题,我创建了一个NSString
类别方法来正确编码字符串。除了[a-zA-Z0-9.-_~]
之外,它将对所有内容进行编码,并将空格编码为+
(根据this specification)。它还将正确处理编码unicode字符。
- (NSString *) URLEncodedString_ch {
NSMutableString * output = [NSMutableString string];
const unsigned char * source = (const unsigned char *)[self UTF8String];
int sourceLen = strlen((const char *)source);
for (int i = 0; i < sourceLen; ++i) {
const unsigned char thisChar = source[i];
if (thisChar == ' '){
[output appendString:@"+"];
} else if (thisChar == '.' || thisChar == '-' || thisChar == '_' || thisChar == '~' ||
(thisChar >= 'a' && thisChar <= 'z') ||
(thisChar >= 'A' && thisChar <= 'Z') ||
(thisChar >= '0' && thisChar <= '9')) {
[output appendFormat:@"%c", thisChar];
} else {
[output appendFormat:@"%%%02X", thisChar];
}
}
return output;
}
答案 1 :(得分:103)
iOS 7 SDK现在有一个更好的stringByAddingPercentEscapesUsingEncoding
替代方案,可以让您指定除了某些允许的字符外,您希望所有字符都被转义。如果您在部分中构建URL,则效果很好:
NSString * unescapedQuery = [[NSString alloc] initWithFormat:@"?myparam=%d", numericParamValue];
NSString * escapedQuery = [unescapedQuery stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
NSString * urlString = [[NSString alloc] initWithFormat:@"http://ExampleOnly.com/path.ext%@", escapedQuery];
虽然URL的其他部分通常不是变量,但NSURLUtilities类别中也有常量:
[NSCharacterSet URLHostAllowedCharacterSet]
[NSCharacterSet URLUserAllowedCharacterSet]
[NSCharacterSet URLPasswordAllowedCharacterSet]
[NSCharacterSet URLPathAllowedCharacterSet]
[NSCharacterSet URLFragmentAllowedCharacterSet]
[NSCharacterSet URLQueryAllowedCharacterSet]
包含网址查询部分中允许的所有字符(以?
开头且片段#
之前的部分(如果有)包括?
和&
或=
字符,用于分隔参数名称和值。对于具有字母数字值的查询参数,任何这些字符都可能包含在用于构建查询字符串的变量值中。在这种情况下,查询字符串的每个部分都需要进行转义,这需要更多的工作:
NSMutableCharacterSet * URLQueryPartAllowedCharacterSet; // possibly defined in class extension ...
// ... and built in init or on first use
URLQueryPartAllowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy];
[URLQueryPartAllowedCharacterSet removeCharactersInString:@"&+=?"]; // %26, %3D, %3F
// then escape variables in the URL, such as values in the query and any fragment:
NSString * escapedValue = [anUnescapedValue stringByAddingPercentEncodingWithAllowedCharacters:URLQueryPartAllowedCharacterSet];
NSString * escapedFrag = [anUnescapedFrag stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]];
NSString * urlString = [[NSString alloc] initWithFormat:@"http://ExampleOnly.com/path.ext?myparam=%@#%@", escapedValue, escapedFrag];
NSURL * url = [[NSURL alloc] initWithString:urlString];
unescapedValue
甚至可以是整个网址,例如回调或重定向:
NSString * escapedCallbackParamValue = [anAlreadyEscapedCallbackURL stringByAddingPercentEncodingWithAllowedCharacters:URLQueryPartAllowedCharacterSet];
NSURL * callbackURL = [[NSURL alloc] initWithString:[[NSString alloc] initWithFormat:@"http://ExampleOnly.com/path.ext?callback=%@", escapedCallbackParamValue]];
注意:不要将NSURL initWithScheme:(NSString *)scheme host:(NSString *)host path:(NSString *)path
用于带有查询字符串的URL,因为它会为路径添加更多百分比转义。
答案 2 :(得分:5)
NSString的stringByAddingPercentEscapesUsingEncoding:看起来就像你追求的那样。
编辑:以下是使用CFURLCreateStringByAddingPercentEscapes
的示例。 originalString
可以是NSString
或CFStringRef
。
CFStringRef newString = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, originalString, NULL, CFSTR("!*'();:@&=+@,/?#[]"), kCFStringEncodingUTF8);
请注意,这是未经测试的。您应该查看documentation page以确保您了解CFStringRef
的内存分配语义,免费桥接的概念等等。
此外,我不知道(在我的头脑中)legalURLCharactersToBeEscaped
参数中指定的哪些字符无论如何都会被转义(由于URL中的非法)。您可能想要检查一下,虽然为了安全起见并直接指定要转义的字符可能更好。
我正在将此答案作为社区维基,以便对CoreFoundation有更多了解的人可以进行改进。
答案 3 :(得分:5)
NSString *encodedString = [myString stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
它不会替换你的字符串内联;它会返回一个新字符串。这个方法以“string”开头的事实暗示了这一点。这是一种基于当前NSString实例化NSString的新实例的便捷方法。
注意 - 新字符串将是autorelease
'd,所以当你完成它时不要在它上面调用release。
答案 4 :(得分:4)
遵循RFC3986标准,以下是我用于编码网址组件的内容:
// https://tools.ietf.org/html/rfc3986#section-2.2
let rfc3986Reserved = NSCharacterSet(charactersInString: "!*'();:@&=+$,/?#[]")
let encoded = "email+with+plus@example.com".stringByAddingPercentEncodingWithAllowedCharacters(rfc3986Reserved.invertedSet)
输出:email%2Bwith%2Bplus%40example.com
答案 5 :(得分:2)
如果您在objective-c程序中使用ASI HttpRequest library,我不能高度推荐,那么您可以在其ASIFormDataRequest对象上使用“encodeURL”辅助API。遗憾的是,API不是静态的,因此可能值得在项目中使用其实现创建扩展。
直接从ASIFormDataRequest.m复制的用于encodeURL实现的代码是:
- (NSString*)encodeURL:(NSString *)string
{
NSString *newString = NSMakeCollectable([(NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)string, NULL, CFSTR(":/?#[]@!$ &'()*+,;=\"<>%{}|\\^~`"), CFStringConvertNSStringEncodingToEncoding([self stringEncoding])) autorelease]);
if (newString) {
return newString;
}
return @"";
}
正如您所看到的,它本质上是CFURLCreateStringByAddingPercentEscapes
的一个包装器,负责处理应该正确转义的所有字符。
答案 6 :(得分:0)
在我发现Rob的答案之前,这个答案似乎运作良好并且因为它更干净而更受欢迎,所以我继续将Dave的答案移植到Swift。如果有人有兴趣,我会把它留在这里:
public extension String {
// For performance, I've replaced the char constants with integers, as char constants don't work in Swift.
var URLEncodedValue: String {
let output = NSMutableString()
guard let source = self.cStringUsingEncoding(NSUTF8StringEncoding) else {
return self
}
let sourceLen = source.count
var i = 0
while i < sourceLen - 1 {
let thisChar = source[i]
if thisChar == 32 {
output.appendString("+")
} else if thisChar == 46 || thisChar == 45 || thisChar == 95 || thisChar == 126 ||
(thisChar >= 97 && thisChar <= 122) ||
(thisChar >= 65 && thisChar <= 90) ||
(thisChar >= 48 && thisChar <= 57) {
output.appendFormat("%c", thisChar)
} else {
output.appendFormat("%%%02X", thisChar)
}
i++
}
return output as String
}
}
答案 7 :(得分:0)
在Swift4中:
var str = "someurl/somecontent"
let percentEncodedString = str.addingPercentEncoding(withAllowedCharacters: .alphanumerics)