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


72

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

百分比编码Wiki

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

myURL = @"someurl/somecontent"

我希望字符串看起来像:

myEncodedURL = @"someurl%2Fsomecontent"

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


您应该将%添加到该列表。
JeremyP,2010年

我不明白您的评论:-s
尚范

他的意思是,%字符本身应该经过URL编码。这样,输入字符串like%2F将变成%252F。我还建议在%20该列表中添加空格字符()。
大卫2010年

Answers:


143

我发现,无论stringByAddingPercentEscapesUsingEncoding:CFURLCreateStringByAddingPercentEscapes()不足。的NSString方法会丢失很多字符,而CF函数只能让您说出要转义的(特定)字符。正确的规范是转义除少数字符外的所有字符。

为了解决这个问题,我创建了一个NSStringcategory方法来正确编码一个字符串。它将对所有内容(除此以外)[a-zA-Z0-9.-_~]进行百分比编码,还将对空格进行编码+(根据此规范)。它还将正确处理编码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;
}

7
请注意,根据x-www-form-urlencoded,将空格编码为+,而不是%20,而不是oauth-1,3.6(您发布的规范链接)。
Jano

6
@mihir当然。这只是一种转换方法。如果您不希望对整个字符串进行编码,请不要传入整个字符串...
Dave DeLong

3
Ya的意思是,实际上我已经实现了所有事情,并且比意识到这个问题更需要现在对每个参数进行单独编码...非常麻烦... :(
Mihir Mehta

正如@Jano所指出的,这里+编码空间不是理想的,而是保留给URL的查询部分。见stackoverflow.com/questions/2678551/…–
levigroker

@zaitsman此网站上所有内容的许可证位于页脚:cc-wiki,需要提供署名。
Dave DeLong

105

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]包括URL的查询部分中允许的所有字符(对于片段而言,该部分以,?和开头#,如果有的话),包括,?&=字符,用于分隔参数名称和值。对于带有字母数字值的查询参数,这些字符中的任何一个都可能包含在用于构建查询字符串的变量的值中。在这种情况下,查询字符串的每个部分都需要转义,这需要做更多的工作:

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甚至可以是一个完整的URL,如回调或重定向:

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,因为它将为路径添加更多的转义百分比。


这应该是主要答案。它是iOS7实用程序的最新版本,它正确地指出URL的不同部分应以不同的方式转义。
algal

1
只是缺少了“ +”,因此您可能想要添加[URLQueryPartAllowedCharacterSet removeCharactersInRange:NSMakeRange('+', 1)];,否则该代码非常适合转义字符串,谢谢!
Quentin G.

9
还有一个比较简单的选择:URLQueryPartAllowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy]; [URLQueryPartAllowedCharacterSet removeCharactersInString:@"?&=@+/'"];我还在Mac上测试了100,000次迭代,发现这始终比removeCharactersInRange:多次调用要快一些。
robotspacer 2014年

怎么样%?如果它在值中,是否也应该编码?
njzk2

5
NSString *encodedString = [myString stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];

它不会替换您的内联字符串;它将返回一个新字符串。该方法以单词“ string”开头的事实暗示了这一点。这是一种基于当前NSString实例化NSString新实例的便捷方法。

注意-新字符串将是autorelease'd,因此在使用完新字符串时不要在其上调用release。


谢谢,但是不起作用,请检查我更新的问题。
藩范

2
结果还是一样,没有变化。
Hoang Pham

5

NSString的stringByAddingPercentEscapesUsingEncoding:看起来像您想要的。

编辑:这是一个使用示例CFURLCreateStringByAddingPercentEscapesoriginalString可以是NSStringCFStringRef

CFStringRef newString = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, originalString, NULL, CFSTR("!*'();:@&=+@,/?#[]"), kCFStringEncodingUTF8);

请注意,这未经测试。您应该查看文档页面,以确保您了解的内存分配语义CFStringRef,免费桥接的概念,等等。

另外,我也不知道(在我的头顶上)legalURLCharactersToBeEscaped参数中指定的哪些字符无论如何都会被转义(由于URL中的非法)。您可能需要检查一下,尽管最好还是安全起见,直接指定要转义的字符。

我将此答案作为社区Wiki,以便对CoreFoundation有更多了解的人们可以进行改进。


我必须使用哪种NSStringEcoding才能使上述所有字符正常工作?您尝试过用琴弦吗?
藩范

嗯,NSStringEncoding您想要的东西似乎没有价值。您可以改用CFURLCreateStringByAddingPercentEscapesdeveloper.apple.com/mac/library/documentation/CoreFoundation/…)-它使您可以直接指定要转义的字符。
大卫,2010年

...哦,顺便说一句:NSStringCFStringRef是“免费桥接”的,这意味着它们可以互换地传递给彼此的功能。
大卫2010年

5

遵循RFC3986标准,这是我用来编码URL组件的内容:

// 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


嗨,阿隆索,我只是编码一个电子邮件地址。我该怎么做?例如,我的邮件地址是m+2@qq.com,如果我编码@,我们的服务器将报告错误。你能帮我一下吗?谢谢
mmm2006 '19

2

如果您正在使用 ASI HttpRequest库在我的Objective-C程序,那么我不能推荐它太多,那么您可以在其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用于处理所有应正确转义的字符。


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
    }
}

1
例如:let string = "te&st"; print(string.URLEncodedValue)
Ben Baron'3

0

在Swift4中:

 var str = "someurl/somecontent"

 let percentEncodedString = str.addingPercentEncoding(withAllowedCharacters: .alphanumerics)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.