【问题标题】:Objective-c iPhone percent encode a string?Objective-c iPhone 百分比编码一个字符串?
【发布时间】:2011-03-26 07:28:07
【问题描述】:

我想得到这些特定字母的百分比编码字符串,如何在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,但它不起作用,结果仍然与原始字符串相同。请指教。

【问题讨论】:

  • 你应该将 % 添加到该列表中。
  • 我不明白你的评论:-s
  • 他的意思是 % 字符本身应该是 URL 编码的。这样,像%2F 这样的输入字符串将变成%252F。我还建议将空格字符 (%20) 添加到该列表中。

标签: iphone objective-c


【解决方案1】:

我发现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;
}

【讨论】:

  • 请注意,将空格编码为 + 而不是 %20 是根据 x-www-form-urlencoded,而不是 oauth-1,3.6(您发布的规范链接)。
  • @mihir 当然。这只是一种转换方法。如果您不想对整个字符串进行编码,请不要传入整个字符串...
  • 你明白你的意思,实际上我已经实现了一切,而不是意识到这个问题,现在需要单独编码每个参数......非常麻烦......:(
  • 正如@Jano 指出的那样,+ 编码空间在这里并不理想,它是为 URL 的查询部分保留的。见stackoverflow.com/questions/2678551/…
  • @zaitsman 本网站所有内容的许可都在页脚:cc-wiki with attribution required.
【解决方案2】:

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

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 的不同部分应该以不同的方式转义。
  • 只是缺少“+”,所以您可能需要添加 [URLQueryPartAllowedCharacterSet removeCharactersInRange:NSMakeRange('+', 1)];,否则此代码非常适合转义字符串,谢谢!
  • 还有一个不那么冗长的替代方案:URLQueryPartAllowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy]; [URLQueryPartAllowedCharacterSet removeCharactersInString:@"?&amp;=@+/'"]; 我还在 Mac 上测试了 100,000 次迭代,发现这始终比多次调用 removeCharactersInRange: 略快。
  • % 怎么样?如果它在值中,它不应该被编码吗?
【解决方案3】:
NSString *encodedString = [myString stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];

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

注意——新字符串将是autorelease'd,所以当你完成它时不要调用它。

【讨论】:

  • 谢谢,但它不起作用,请检查我更新的问题。
  • 结果还是一样,没有变化。
【解决方案4】:

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

编辑:这是一个使用CFURLCreateStringByAddingPercentEscapes 代替的示例。 originalString 可以是 NSStringCFStringRef

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

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

另外,我不知道(在我脑海中)legalURLCharactersToBeEscaped 参数中指定的哪些字符无论如何都会被转义(因为在 URL 中是非法的)。您可能想检查一下,尽管为了安全起见,直接指定要转义的字符可能会更好。

我将此答案设为社区 wiki,以便对 CoreFoundation 有更多了解的人可以做出改进。

【讨论】:

  • 我必须使用哪个 NSStringEcoding 才能使上述所有字符正常工作?你试过用字符串吗?
  • 嗯,您想要的似乎没有 NSStringEncoding 值。你可以试试CFURLCreateStringByAddingPercentEscapes (developer.apple.com/mac/library/documentation/CoreFoundation/…) - 它可以让你直接指定要转义的字符。
  • ...哦,顺便说一句:NSStringCFStringRef 是“免费桥接”,这意味着它们可以互换地传递给彼此的函数。
【解决方案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,如果我编码@,我们的服务器会报错。你能帮我解决这个问题吗?谢谢
【解决方案6】:

如果您在自己的 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 的包装器,负责处理所有应正确转义的字符。

【讨论】:

    【解决方案7】:

    在我注意到 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
        }
    }
    

    【讨论】:

    • 例如:let string = "te&amp;st"; print(string.URLEncodedValue)
    【解决方案8】:

    在 Swift4 中:

     var str = "someurl/somecontent"
    
     let percentEncodedString = str.addingPercentEncoding(withAllowedCharacters: .alphanumerics)
    

    【讨论】:

      猜你喜欢
      • 2023-03-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多