NSString Category for better percent escaping
-
15-06-2021 - |
Frage
I wrote a little NSString category with a better implementation of percent escaping for one of my projects: @im
plementation NSString (Escaping)
- (NSString *)stringByAddingSafePercentEscapesUsingEncoding:(NSStringEncoding)encoding
{
NSRange wholeString = NSMakeRange(0, [self length]);
NSMutableString *escaping = [NSMutableString stringWithString:[self stringByAddingPercentEscapesUsingEncoding:encoding]];
[escaping replaceOccurrencesOfString:@"$" withString:@"%24" options:NSCaseInsensitiveSearch range:wholeString];
[escaping replaceOccurrencesOfString:@"&" withString:@"%26" options:NSCaseInsensitiveSearch range:wholeString];
[escaping replaceOccurrencesOfString:@"+" withString:@"%2B" options:NSCaseInsensitiveSearch range:wholeString];
[escaping replaceOccurrencesOfString:@"," withString:@"%2C" options:NSCaseInsensitiveSearch range:wholeString];
[escaping replaceOccurrencesOfString:@"/" withString:@"%2F" options:NSCaseInsensitiveSearch range:wholeString];
[escaping replaceOccurrencesOfString:@":" withString:@"%3A" options:NSCaseInsensitiveSearch range:wholeString];
[escaping replaceOccurrencesOfString:@";" withString:@"%3B" options:NSCaseInsensitiveSearch range:wholeString];
[escaping replaceOccurrencesOfString:@"-" withString:@"%2D" options:NSCaseInsensitiveSearch range:wholeString];
[escaping replaceOccurrencesOfString:@"=" withString:@"%3D" options:NSCaseInsensitiveSearch range:wholeString];
[escaping replaceOccurrencesOfString:@"?" withString:@"%3F" options:NSCaseInsensitiveSearch range:wholeString];
[escaping replaceOccurrencesOfString:@"@" withString:@"%40" options:NSCaseInsensitiveSearch range:wholeString];
[escaping replaceOccurrencesOfString:@" " withString:@"%20" options:NSCaseInsensitiveSearch range:wholeString];
[escaping replaceOccurrencesOfString:@"\t" withString:@"%09" options:NSCaseInsensitiveSearch range:wholeString];
[escaping replaceOccurrencesOfString:@"#" withString:@"%23" options:NSCaseInsensitiveSearch range:wholeString];
[escaping replaceOccurrencesOfString:@"<" withString:@"%3C" options:NSCaseInsensitiveSearch range:wholeString];
[escaping replaceOccurrencesOfString:@">" withString:@"%3E" options:NSCaseInsensitiveSearch range:wholeString];
[escaping replaceOccurrencesOfString:@"\"" withString:@"%22" options:NSCaseInsensitiveSearch range:wholeString];
[escaping replaceOccurrencesOfString:@"\n" withString:@"%0A" options:NSCaseInsensitiveSearch range:wholeString];
NSString *escaped = [NSString stringWithString:escaping];
return escaped;
}
@end
In this one project it works GREAT and I love to use it. But when I port it to another project, that code throughs an EXC_BAD_ACCESS. Could I make something better in that code?
Lösung
I just found my problem by accident:
Since the string effectively changes its length when replacing one characters by three characters, the string length changes with every replace. So the correct implementation for the category would be:
@implementation NSString (Escaping)
- (NSString *)stringByAddingSafePercentEscapesUsingEncoding:(NSStringEncoding)encoding
{
NSRange wholeString = NSMakeRange(0, [self length]);
NSMutableString *escaping = [NSMutableString stringWithString:[self stringByAddingPercentEscapesUsingEncoding:encoding]];
[escaping replaceOccurrencesOfString:@"$" withString:@"%24" options:NSCaseInsensitiveSearch range:wholeString];
wholeString = NSMakeRange(0, [escaping length]);
[escaping replaceOccurrencesOfString:@"&" withString:@"%26" options:NSCaseInsensitiveSearch range:wholeString];
wholeString = NSMakeRange(0, [escaping length]);
[escaping replaceOccurrencesOfString:@"+" withString:@"%2B" options:NSCaseInsensitiveSearch range:wholeString];
wholeString = NSMakeRange(0, [escaping length]);
[escaping replaceOccurrencesOfString:@"," withString:@"%2C" options:NSCaseInsensitiveSearch range:wholeString];
wholeString = NSMakeRange(0, [escaping length]);
[escaping replaceOccurrencesOfString:@"/" withString:@"%2F" options:NSCaseInsensitiveSearch range:wholeString];
wholeString = NSMakeRange(0, [escaping length]);
[escaping replaceOccurrencesOfString:@":" withString:@"%3A" options:NSCaseInsensitiveSearch range:wholeString];
wholeString = NSMakeRange(0, [escaping length]);
[escaping replaceOccurrencesOfString:@";" withString:@"%3B" options:NSCaseInsensitiveSearch range:wholeString];
wholeString = NSMakeRange(0, [escaping length]);
[escaping replaceOccurrencesOfString:@"-" withString:@"%2D" options:NSCaseInsensitiveSearch range:wholeString];
wholeString = NSMakeRange(0, [escaping length]);
[escaping replaceOccurrencesOfString:@"=" withString:@"%3D" options:NSCaseInsensitiveSearch range:wholeString];
wholeString = NSMakeRange(0, [escaping length]);
[escaping replaceOccurrencesOfString:@"?" withString:@"%3F" options:NSCaseInsensitiveSearch range:wholeString];
wholeString = NSMakeRange(0, [escaping length]);
[escaping replaceOccurrencesOfString:@"@" withString:@"%40" options:NSCaseInsensitiveSearch range:wholeString];
wholeString = NSMakeRange(0, [escaping length]);
[escaping replaceOccurrencesOfString:@" " withString:@"%20" options:NSCaseInsensitiveSearch range:wholeString];
wholeString = NSMakeRange(0, [escaping length]);
[escaping replaceOccurrencesOfString:@"\t" withString:@"%09" options:NSCaseInsensitiveSearch range:wholeString];
wholeString = NSMakeRange(0, [escaping length]);
[escaping replaceOccurrencesOfString:@"#" withString:@"%23" options:NSCaseInsensitiveSearch range:wholeString];
wholeString = NSMakeRange(0, [escaping length]);
[escaping replaceOccurrencesOfString:@"<" withString:@"%3C" options:NSCaseInsensitiveSearch range:wholeString];
wholeString = NSMakeRange(0, [escaping length]);
[escaping replaceOccurrencesOfString:@">" withString:@"%3E" options:NSCaseInsensitiveSearch range:wholeString];
wholeString = NSMakeRange(0, [escaping length]);
[escaping replaceOccurrencesOfString:@"\"" withString:@"%22" options:NSCaseInsensitiveSearch range:wholeString];
wholeString = NSMakeRange(0, [escaping length]);
[escaping replaceOccurrencesOfString:@"\n" withString:@"%0A" options:NSCaseInsensitiveSearch range:wholeString];
NSString *escaped = [NSString stringWithString:escaping];
return escaped;
}
@end
Andere Tipps
I think it should be:
NSRange wholeString = NSMakeRange(0, [self length]-1);
You would get 'bad access' if your replaceOccurrencesOfString:withString:options:range: method iterates past the last character in the string.
Or you could use something like this for your replacement methods which already figures on iterating over the whole length. I am not sure what the default search option is however:
escaping = [escaping stringByReplacingOccurencesOfString:@"$" withString:@"%24"];