Objective-c iPhone百分比编码一个字符串?

时间:2010-08-06 12:02:47

标签: iphone objective-c

我想获取这些特定字母的百分比编码字符串,如何在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

Percent-encoding wiki

请使用此字符串进行测试,看看它是否有效:

myURL = @"someurl/somecontent"

我希望字符串看起来像:

myEncodedURL = @"someurl%2Fsomecontent"

我已尝试使用stringByAddingPercentEscapesUsingEncoding: NSASCIIStringEncoding但它不起作用,结果仍然与原始字符串相同。请指教。

8 个答案:

答案 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可以是NSStringCFStringRef

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)