Error while trying to access Google translate with NSURL - objective-c

If I try this code:
NSError *err = nil;
NSString *resp = [NSString stringWithContentsOfURL:[NSURL URLWithString:#"https://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=abdicate&langpair=en|es"]
encoding:NSASCIIStringEncoding
error:&err];
Err contains the following message: Error Domain=NSCocoaErrorDomain Code=258 UserInfo=0x1001166d0 "The file name is invalid."
Anybody knows what this means? If I try the url on a browser, it works fine.

(Using NSData) you can just replace the 1.0 with 1%2E0 and the | with %7C. It is possible that with the period is being interpreted as a file with an extension, thus the 258 error (From Foundation Constants Reference):
NSError Codes
NSError codes in the Cocoa error domain.
enum {
...
NSFileReadInvalidFileNameError = 258,
}
NSFileReadInvalidFileNameError:
Read error because of an invalid file
name Available in Mac OS X v10.4 and later. Declared in
FoundationErrors.h.
Try this:
- (void)viewDidLoad {
[super viewDidLoad];
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"https://ajax.googleapis.com/ajax/services/language/translate?v=1%2E0&q=abdicate&langpair=en%7Ces"]];
if (data) {
NSLog([[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
}
}
Gives the output:
2011-09-27 15:38:05.691 Project[44290:fb03] {"responseData": {"translatedText":"abdicar"}, "responseDetails": null, "responseStatus": 200}
Which is the same as what you get by navigating to the url https://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=abdicate&langpair=en|es in a browser.
Try using NSData, then converting to a string, like so:
NSString *result;
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"https://ajax.googleapis.com/ajax/services/language/translate?%#", [self urlencode:#"v=1.0&q=abdicate&langpair=en|es"]]]];
if (data) {
result = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
} else {
// Do something
}
This is the urlencode method used above:
- (NSString *)urlencode:(NSString *)str {
NSMutableString *output = [NSMutableString string];
const unsigned char *source = (const unsigned char *)[str 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;
}
This whole part was unnecessary, just need to replace the 1.0 and | instead of encoding the entire url.

Related

Encode URL parameters in objective c [duplicate]

I have a NSString like this:
http://www.
but I want to transform it to:
http%3A%2F%2Fwww.
How can I do this?
To escape the characters you want is a little more work.
Example code
iOS7 and above:
NSString *unescaped = #"http://www";
NSString *escapedString = [unescaped stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
NSLog(#"escapedString: %#", escapedString);
NSLog output:
escapedString: http%3A%2F%2Fwww
The following are useful URL encoding character sets:
URLFragmentAllowedCharacterSet "#%<>[\]^`{|}
URLHostAllowedCharacterSet "#%/<>?#\^`{|}
URLPasswordAllowedCharacterSet "#%/:<>?#[\]^`{|}
URLPathAllowedCharacterSet "#%;<>?[\]^`{|}
URLQueryAllowedCharacterSet "#%<>[\]^`{|}
URLUserAllowedCharacterSet "#%/:<>?#[\]^`
Creating a characterset combining all of the above:
NSCharacterSet *URLCombinedCharacterSet = [[NSCharacterSet characterSetWithCharactersInString:#" \"#%/:<>?#[\\]^`{|}"] invertedSet];
Creating a Base64
In the case of Base64 characterset:
NSCharacterSet *URLBase64CharacterSet = [[NSCharacterSet characterSetWithCharactersInString:#"/+=\n"] invertedSet];
For Swift 3.0:
var escapedString = originalString.addingPercentEncoding(withAllowedCharacters:.urlHostAllowed)
For Swift 2.x:
var escapedString = originalString.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLHostAllowedCharacterSet())
Note: stringByAddingPercentEncodingWithAllowedCharacters will also encode UTF-8 characters needing encoding.
Pre iOS7 use Core Foundation
Using Core Foundation With ARC:
NSString *escapedString = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(
NULL,
(__bridge CFStringRef) unescaped,
NULL,
CFSTR("!*'();:#&=+$,/?%#[]\" "),
kCFStringEncodingUTF8));
Using Core Foundation Without ARC:
NSString *escapedString = (NSString *)CFURLCreateStringByAddingPercentEscapes(
NULL,
(CFStringRef)unescaped,
NULL,
CFSTR("!*'();:#&=+$,/?%#[]\" "),
kCFStringEncodingUTF8);
Note: -stringByAddingPercentEscapesUsingEncoding will not produce the correct encoding, in this case it will not encode anything returning the same string.
stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding encodes 14 characrters:
`#%^{}[]|\"<> plus the space character as percent escaped.
testString:
" `~!##$%^&*()_+-={}[]|\\:;\"'<,>.?/AZaz"
encodedString:
"%20%60~!#%23$%25%5E&*()_+-=%7B%7D%5B%5D%7C%5C:;%22'%3C,%3E.?/AZaz"
Note: consider if this set of characters meet your needs, if not change them as needed.
RFC 3986 characters requiring encoding (% added since it is the encoding prefix character):
"!#$&'()*+,/:;=?#[]%"
Some "unreserved characters" are additionally encoded:
"\n\r \"%-.<>\^_`{|}~"
It's called URL encoding. More here.
-(NSString *)urlEncodeUsingEncoding:(NSStringEncoding)encoding {
return (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,
(CFStringRef)self,
NULL,
(CFStringRef)#"!*'\"();:#&=+$,/?%#[]% ",
CFStringConvertNSStringEncodingToEncoding(encoding));
}
This is not my solution. Someone else wrote in stackoverflow but I have forgotten how.
Somehow this solution works "well". It handles diacritic, chinese characters, and pretty much anything else.
- (NSString *) URLEncodedString {
NSMutableString * output = [NSMutableString string];
const char * source = [self UTF8String];
int sourceLen = strlen(source);
for (int i = 0; i < sourceLen; ++i) {
const unsigned char thisChar = (const unsigned char)source[i];
if (false && 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;
}
If someone would tell me who wrote this code, I'll really appreciate it. Basically he has some explanation why this encoded string will decode exactly as it wish.
I modified his solution a little. I like space to be represented with %20 rather than +. That's all.
NSString * encodedString = (NSString *)CFURLCreateStringByAddingPercentEscapes(NUL,(CFStringRef)#"parameter",NULL,(CFStringRef)#"!*'();#&+$,/?%#[]~=_-.:",kCFStringEncodingUTF8 );
NSURL * url = [[NSURL alloc] initWithString:[#"address here" stringByAppendingFormat:#"?cid=%#",encodedString, nil]];
This can work in Objective C ARC.Use CFBridgingRelease to cast a Core Foundation-style object as an Objective-C object and transfer ownership of the object to ARC .See Function CFBridgingRelease here.
+ (NSString *)encodeUrlString:(NSString *)string {
return CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes
(kCFAllocatorDefault,
(__bridge CFStringRef)string,
NULL,
CFSTR("!*'();:#&=+$,/?%#[]"),
kCFStringEncodingUTF8)
);}
Swift iOS:
Just For Information : I have used this:
extension String {
func urlEncode() -> CFString {
return CFURLCreateStringByAddingPercentEscapes(
nil,
self,
nil,
"!*'();:#&=+$,/?%#[]",
CFStringBuiltInEncodings.UTF8.rawValue
)
}
}// end extension String
Here's what I use. Note you have to use the #autoreleasepool feature or the program might crash or lockup the IDE. I had to restart my IDE three times until I realized the fix. It appears that this code is ARC compliant.
This question has been asked many times, and many answers given, but sadly all of the ones selected (and a few others suggested) are wrong.
Here's the test string that I used: This is my 123+ test & test2. Got it?!
These are my Objective C++ class methods:
static NSString * urlDecode(NSString *stringToDecode) {
NSString *result = [stringToDecode stringByReplacingOccurrencesOfString:#"+" withString:#" "];
result = [result stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
return result;
}
static NSString * urlEncode(NSString *stringToEncode) {
#autoreleasepool {
NSString *result = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(
NULL,
(CFStringRef)stringToEncode,
NULL,
(CFStringRef)#"!*'\"();:#&=+$,/?%#[]% ",
kCFStringEncodingUTF8
));
result = [result stringByReplacingOccurrencesOfString:#"%20" withString:#"+"];
return result;
}
}
NSString *str = (NSString *)CFURLCreateStringByAddingPercentEscapes(
NULL,
(CFStringRef)yourString,
NULL,
CFSTR("/:"),
kCFStringEncodingUTF8);
You will need to release or autorelease str yourself.
Google implements this in their Google Toolbox for Mac. So that's a good place to peak how they're doing it. Another option is to include the Toolbox and use their implementation.
Checkout the implementation here. (Which comes down to exactly what people have been posting here).
This is how I am doing this in swift.
extension String {
func encodeURIComponent() -> String {
return self.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!
}
func decodeURIComponent() -> String {
return self.componentsSeparatedByString("+").joinWithSeparator(" ").stringByRemovingPercentEncoding!
}
}
This is what I did on Swift 5:
func formatPassword() -> String {
var output = "";
for ch in self {
let char = String(ch)
switch ch {
case " ":
output.append("+")
break
case ".", "-", "_", "~", "a"..."z", "A"..."Z", "0"..."9":
output.append(char)
break
default:
print(ch)
let unicode = char.unicodeScalars.first?.value ?? 0
let unicodeValue = NSNumber(value: unicode).intValue
let hexValue = String(format: "%02X", arguments: [unicodeValue])
output = output.appendingFormat("%%%#", hexValue)
}
}
return output as String
}
Then I called this function where I defined my password.
//use NSString instance method like this:
+ (NSString *)encodeURIComponent:(NSString *)string
{
NSString *s = [string stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
return s;
}
+ (NSString *)decodeURIComponent:(NSString *)string
{
NSString *s = [string stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
return s;
}
remember,you should only do encode or decode for your parameter value, not all the url you request.
int strLength = 0;
NSString *urlStr = #"http://www";
NSLog(#" urlStr : %#", urlStr );
NSMutableString *mutableUrlStr = [urlStr mutableCopy];
NSLog(#" mutableUrlStr : %#", mutableUrlStr );
strLength = [mutableUrlStr length];
[mutableUrlStr replaceOccurrencesOfString:#":" withString:#"%3A" options:NSCaseInsensitiveSearch range:NSMakeRange(0, strLength)];
NSLog(#" mutableUrlStr : %#", mutableUrlStr );
strLength = [mutableUrlStr length];
[mutableUrlStr replaceOccurrencesOfString:#"/" withString:#"%2F" options:NSCaseInsensitiveSearch range:NSMakeRange(0, strLength)];
NSLog(#" mutableUrlStr : %#", mutableUrlStr );

Convert NSString in ASCII [duplicate]

I have a NSString like this:
http://www.
but I want to transform it to:
http%3A%2F%2Fwww.
How can I do this?
To escape the characters you want is a little more work.
Example code
iOS7 and above:
NSString *unescaped = #"http://www";
NSString *escapedString = [unescaped stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
NSLog(#"escapedString: %#", escapedString);
NSLog output:
escapedString: http%3A%2F%2Fwww
The following are useful URL encoding character sets:
URLFragmentAllowedCharacterSet "#%<>[\]^`{|}
URLHostAllowedCharacterSet "#%/<>?#\^`{|}
URLPasswordAllowedCharacterSet "#%/:<>?#[\]^`{|}
URLPathAllowedCharacterSet "#%;<>?[\]^`{|}
URLQueryAllowedCharacterSet "#%<>[\]^`{|}
URLUserAllowedCharacterSet "#%/:<>?#[\]^`
Creating a characterset combining all of the above:
NSCharacterSet *URLCombinedCharacterSet = [[NSCharacterSet characterSetWithCharactersInString:#" \"#%/:<>?#[\\]^`{|}"] invertedSet];
Creating a Base64
In the case of Base64 characterset:
NSCharacterSet *URLBase64CharacterSet = [[NSCharacterSet characterSetWithCharactersInString:#"/+=\n"] invertedSet];
For Swift 3.0:
var escapedString = originalString.addingPercentEncoding(withAllowedCharacters:.urlHostAllowed)
For Swift 2.x:
var escapedString = originalString.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLHostAllowedCharacterSet())
Note: stringByAddingPercentEncodingWithAllowedCharacters will also encode UTF-8 characters needing encoding.
Pre iOS7 use Core Foundation
Using Core Foundation With ARC:
NSString *escapedString = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(
NULL,
(__bridge CFStringRef) unescaped,
NULL,
CFSTR("!*'();:#&=+$,/?%#[]\" "),
kCFStringEncodingUTF8));
Using Core Foundation Without ARC:
NSString *escapedString = (NSString *)CFURLCreateStringByAddingPercentEscapes(
NULL,
(CFStringRef)unescaped,
NULL,
CFSTR("!*'();:#&=+$,/?%#[]\" "),
kCFStringEncodingUTF8);
Note: -stringByAddingPercentEscapesUsingEncoding will not produce the correct encoding, in this case it will not encode anything returning the same string.
stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding encodes 14 characrters:
`#%^{}[]|\"<> plus the space character as percent escaped.
testString:
" `~!##$%^&*()_+-={}[]|\\:;\"'<,>.?/AZaz"
encodedString:
"%20%60~!#%23$%25%5E&*()_+-=%7B%7D%5B%5D%7C%5C:;%22'%3C,%3E.?/AZaz"
Note: consider if this set of characters meet your needs, if not change them as needed.
RFC 3986 characters requiring encoding (% added since it is the encoding prefix character):
"!#$&'()*+,/:;=?#[]%"
Some "unreserved characters" are additionally encoded:
"\n\r \"%-.<>\^_`{|}~"
It's called URL encoding. More here.
-(NSString *)urlEncodeUsingEncoding:(NSStringEncoding)encoding {
return (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,
(CFStringRef)self,
NULL,
(CFStringRef)#"!*'\"();:#&=+$,/?%#[]% ",
CFStringConvertNSStringEncodingToEncoding(encoding));
}
This is not my solution. Someone else wrote in stackoverflow but I have forgotten how.
Somehow this solution works "well". It handles diacritic, chinese characters, and pretty much anything else.
- (NSString *) URLEncodedString {
NSMutableString * output = [NSMutableString string];
const char * source = [self UTF8String];
int sourceLen = strlen(source);
for (int i = 0; i < sourceLen; ++i) {
const unsigned char thisChar = (const unsigned char)source[i];
if (false && 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;
}
If someone would tell me who wrote this code, I'll really appreciate it. Basically he has some explanation why this encoded string will decode exactly as it wish.
I modified his solution a little. I like space to be represented with %20 rather than +. That's all.
NSString * encodedString = (NSString *)CFURLCreateStringByAddingPercentEscapes(NUL,(CFStringRef)#"parameter",NULL,(CFStringRef)#"!*'();#&+$,/?%#[]~=_-.:",kCFStringEncodingUTF8 );
NSURL * url = [[NSURL alloc] initWithString:[#"address here" stringByAppendingFormat:#"?cid=%#",encodedString, nil]];
This can work in Objective C ARC.Use CFBridgingRelease to cast a Core Foundation-style object as an Objective-C object and transfer ownership of the object to ARC .See Function CFBridgingRelease here.
+ (NSString *)encodeUrlString:(NSString *)string {
return CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes
(kCFAllocatorDefault,
(__bridge CFStringRef)string,
NULL,
CFSTR("!*'();:#&=+$,/?%#[]"),
kCFStringEncodingUTF8)
);}
Swift iOS:
Just For Information : I have used this:
extension String {
func urlEncode() -> CFString {
return CFURLCreateStringByAddingPercentEscapes(
nil,
self,
nil,
"!*'();:#&=+$,/?%#[]",
CFStringBuiltInEncodings.UTF8.rawValue
)
}
}// end extension String
Here's what I use. Note you have to use the #autoreleasepool feature or the program might crash or lockup the IDE. I had to restart my IDE three times until I realized the fix. It appears that this code is ARC compliant.
This question has been asked many times, and many answers given, but sadly all of the ones selected (and a few others suggested) are wrong.
Here's the test string that I used: This is my 123+ test & test2. Got it?!
These are my Objective C++ class methods:
static NSString * urlDecode(NSString *stringToDecode) {
NSString *result = [stringToDecode stringByReplacingOccurrencesOfString:#"+" withString:#" "];
result = [result stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
return result;
}
static NSString * urlEncode(NSString *stringToEncode) {
#autoreleasepool {
NSString *result = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(
NULL,
(CFStringRef)stringToEncode,
NULL,
(CFStringRef)#"!*'\"();:#&=+$,/?%#[]% ",
kCFStringEncodingUTF8
));
result = [result stringByReplacingOccurrencesOfString:#"%20" withString:#"+"];
return result;
}
}
NSString *str = (NSString *)CFURLCreateStringByAddingPercentEscapes(
NULL,
(CFStringRef)yourString,
NULL,
CFSTR("/:"),
kCFStringEncodingUTF8);
You will need to release or autorelease str yourself.
Google implements this in their Google Toolbox for Mac. So that's a good place to peak how they're doing it. Another option is to include the Toolbox and use their implementation.
Checkout the implementation here. (Which comes down to exactly what people have been posting here).
This is how I am doing this in swift.
extension String {
func encodeURIComponent() -> String {
return self.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!
}
func decodeURIComponent() -> String {
return self.componentsSeparatedByString("+").joinWithSeparator(" ").stringByRemovingPercentEncoding!
}
}
This is what I did on Swift 5:
func formatPassword() -> String {
var output = "";
for ch in self {
let char = String(ch)
switch ch {
case " ":
output.append("+")
break
case ".", "-", "_", "~", "a"..."z", "A"..."Z", "0"..."9":
output.append(char)
break
default:
print(ch)
let unicode = char.unicodeScalars.first?.value ?? 0
let unicodeValue = NSNumber(value: unicode).intValue
let hexValue = String(format: "%02X", arguments: [unicodeValue])
output = output.appendingFormat("%%%#", hexValue)
}
}
return output as String
}
Then I called this function where I defined my password.
//use NSString instance method like this:
+ (NSString *)encodeURIComponent:(NSString *)string
{
NSString *s = [string stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
return s;
}
+ (NSString *)decodeURIComponent:(NSString *)string
{
NSString *s = [string stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
return s;
}
remember,you should only do encode or decode for your parameter value, not all the url you request.
int strLength = 0;
NSString *urlStr = #"http://www";
NSLog(#" urlStr : %#", urlStr );
NSMutableString *mutableUrlStr = [urlStr mutableCopy];
NSLog(#" mutableUrlStr : %#", mutableUrlStr );
strLength = [mutableUrlStr length];
[mutableUrlStr replaceOccurrencesOfString:#":" withString:#"%3A" options:NSCaseInsensitiveSearch range:NSMakeRange(0, strLength)];
NSLog(#" mutableUrlStr : %#", mutableUrlStr );
strLength = [mutableUrlStr length];
[mutableUrlStr replaceOccurrencesOfString:#"/" withString:#"%2F" options:NSCaseInsensitiveSearch range:NSMakeRange(0, strLength)];
NSLog(#" mutableUrlStr : %#", mutableUrlStr );

creating NSMutableString from two other NSMutableStrings

I have two strings:
#"--U" and #"-O-" and would like to create another NSMutableString that makes #"-OU" using the two givens. Does anyone know how I can do this?
Note, the following code assumes that s1 and s2 have the same length, otherwise it will throw an exception at some point, so do the checking :)
- (NSMutableString *)concatString:(NSString *)s1 withString:(NSString *)s2
{
NSMutableString *result = [NSMutableString stringWithCapacity:[s1 length]];
for (int i = 0; i < [s1 length]; i++) {
unichar c = [s1 characterAtIndex:i];
if ( c != '-' ) {
[result appendFormat:#"%c", c];
}
else {
[result appendFormat:#"%c", [s2 characterAtIndex:i]];
}
}
return result;
}
NSString *t1=#"-0-";
NSString *t2=#"--U";
NSString *temp1=[t1 substringWithRange:NSMakeRange(0, 2)];
NSString *temp2=[t2 substringFromIndex:2];
NSLog(#"%#",[NSString stringWithFormat:#"%#%#",temp1,temp2]);
This version is a bit more long-winded than Nick's, but breaks the thing down into C functions and tail recursion, so it may run faster. It also handles strings of different lengths, choosing to mirror the shorter string's length.
NOTE: I have not run this code yet, so it may be buggy or be missing something obvious.
void recursiveStringMerge(unichar* string1, unichar* string2, unichar* result) {
if (string1[0] == '\0' || string2[0] == '\0') {
result[0] = '\0'; //properly end the string
return; //no use in trying to add more to this string
}
else if (string1[0] != '-') {
result[0] = string1[0];
}
else {
result[0] = string2[0];
}
//move on to the next unichar in each array
recursiveStringMerge(string1+1, string2+1, result+1);
}
- (NSMutableString *)concatString:(NSString *)s1 withString:(NSString *)s2 {
NSUInteger resultLength;
NSUInteger s1Length = [s1 length]+1; //ensure space for NULL with the +1
NSUInteger s2Length = [s2 length]+1;
resultLength = (s1Length <= s2Length) ? s1Length : s2Length; //only need the shortest
unichar* result = malloc(resultLength*sizeof(unichar));
unichar *string1 = calloc(s1Length, sizeof(unichar));
[s1 getCharacters:buffer];
unichar *string2 = calloc(s2Length, sizeof(unichar));
[s2 getCharacters:buffer];
recursiveStringMerge(string1, string2, result);
return [NSString stringWithCharacters: result length: resultLength];
}

Problem with the dateformatter's in Iphone sdk

Here I had a problem with the date formatters actually I had an option to search events based on the date.for this Im comparing the date with the date store in the database and getting the event based on the date for this I wrote the code as follows:
-(NSMutableArray*)getSearchAllLists:(EventsList*)aEvent
{
[searchList removeAllObjects];
EventsList *searchEvent = nil;
const char* sql;
NSString *conditionStr = #" where ";
if([aEvent.eventName length] > 0)
{
NSString *str = #"'%";
str = [str stringByAppendingString:aEvent.eventName];
str = [str stringByAppendingString:#"%'"];
conditionStr = [conditionStr stringByAppendingString:[NSString stringWithFormat:#" eventName like %s",[str UTF8String]]];
}
if([aEvent.eventWineName length]>0)
{
NSString *str = #"'%";
str = [str stringByAppendingString:aEvent.eventWineName];
str = [str stringByAppendingString:#"%'"];
if([aEvent.eventName length]>0)
{
conditionStr = [conditionStr stringByAppendingString:[NSString stringWithFormat:#" and (wineName like %s)",[str UTF8String]]];
}
else
{
conditionStr = [conditionStr stringByAppendingString:[NSString stringWithFormat:#"wineName like %s",[str UTF8String]]];
}
}
if([aEvent.eventVariety length]>0)
{
NSString *str = #"'%";
str = [str stringByAppendingString:aEvent.eventVariety];
str = [str stringByAppendingString:#"%'"];
if([aEvent.eventName length]>0 || [aEvent.eventWineName length]>0)
{
conditionStr = [conditionStr stringByAppendingString:[NSString stringWithFormat:#" and (variety like %s)",[str UTF8String]]];
}
else
{
conditionStr = [conditionStr stringByAppendingString:[NSString stringWithFormat:#"variety like %s" ,[str UTF8String]]];
}
}
if([aEvent.eventWinery length]>0)
{
NSString *str = #"'%";
str = [str stringByAppendingString:aEvent.eventWinery];
str = [str stringByAppendingString:#"%'"];
if([aEvent.eventName length]>0 || [aEvent.eventWineName length]>0 || [aEvent.eventVariety length]>0)
{
conditionStr = [conditionStr stringByAppendingString:[NSString stringWithFormat:#" and (winery like %s)",[str UTF8String]]];
}
else
{
conditionStr = [conditionStr stringByAppendingString:[NSString stringWithFormat:#"winery like %s" ,[str UTF8String]]];
}
}
if(aEvent.eventRatings >0)
{
if([aEvent.eventName length]>0 || [aEvent.eventWineName length]>0 || [aEvent.eventWinery length]>0 || [aEvent.eventVariety length]>0)
{
conditionStr = [conditionStr stringByAppendingString:[NSString stringWithFormat:#" and ratings = %d",aEvent.eventRatings]];
}
else
{
conditionStr = [conditionStr stringByAppendingString:[NSString stringWithFormat:#" ratings = %d",aEvent.eventRatings]];
}
}
if(aEvent.eventDate >0)
{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:#"dd-MMM-yy 23:59:59"];
NSString *dateStr = [dateFormatter stringFromDate:aEvent.eventDate];
NSDate *date = [dateFormatter dateFromString:dateStr];
[dateFormatter release];
NSTimeInterval interval = [date timeIntervalSinceReferenceDate];
printf("\n Interval in advance search:%f",interval);
if([aEvent.eventName length]>0 || [aEvent.eventWineName length]>0 || [aEvent.eventWinery length]>0 || aEvent.eventRatings>0 || [aEvent.eventVariety length]>0)
{
conditionStr = [conditionStr stringByAppendingString:[NSString stringWithFormat:#" and eventDate = %f",interval]];
}
else
{
conditionStr = [conditionStr stringByAppendingString:[NSString stringWithFormat:#" eventDate = %f",interval]];
}
}
NSString* queryString= #" select * from event";
queryString = [queryString stringByAppendingString:conditionStr];
sqlite3_stmt* statement;
sql = (char*)[queryString UTF8String];
if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) != SQLITE_OK)
{
NSAssert1(0, #"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(database));
}
sqlite3_bind_text(statement, 1, [aEvent.eventName UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(statement, 2, [aEvent.eventWineName UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(statement, 3, [aEvent.eventVariety UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(statement, 4, [aEvent.eventWinery UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_int(statement, 5,aEvent.eventRatings);
sqlite3_bind_double(statement,6, [[aEvent eventDate] timeIntervalSinceReferenceDate]);
while (sqlite3_step(statement) == SQLITE_ROW)
{
primaryKey = sqlite3_column_int(statement, 0);
searchEvent = [[EventsList alloc] initWithPrimaryKey:primaryKey database:database];
[searchList addObject:searchEvent];
[searchEvent release];
}
sqlite3_finalize(statement);
return searchList;
}
Here I compared the interval value in the database and the date we are searching and Im getting the different values and results were not found.
Guy's help me to get rid of this.
Anyone's help will be much appreciated.
Thank you
Monish Calapatapu.
I'm finding it hard to follow the problem. Can you re-edit the sample code down to just the lines that show the problem? Also an example of what values you are inputting and getting out would help as well.

CamelCase to underscores and back in Objective-C

I'm looking for a simple, efficient way to convert strings in CamelCase to underscore notation (i.e., MyClassName -> my_class_name) and back again in Objective C.
My current solution involves lots of rangeOfString, characterAtIndex, and replaceCharactersInRange operations on NSMutableStrings, and is just plain ugly as hell :) It seems that there must be a better solution, but I'm not sure what it is.
I'd rather not import a regex library just for this one use case, though that is an option if all else fails.
Chris's suggestion of RegexKitLite is good. It's an excellent toolkit, but this could be done pretty easily with NSScanner. Use -scanCharactersFromSet:intoString: alternating between +uppercaseLetterCharacterSet and +lowercaseLetterCharacterSet. For going back, you'd use -scanUpToCharactersFromSet: instead, using a character set with just an underscore in it.
How about these:
NSString *MyCamelCaseToUnderscores(NSString *input) {
NSMutableString *output = [NSMutableString string];
NSCharacterSet *uppercase = [NSCharacterSet uppercaseLetterCharacterSet];
for (NSInteger idx = 0; idx < [input length]; idx += 1) {
unichar c = [input characterAtIndex:idx];
if ([uppercase characterIsMember:c]) {
[output appendFormat:#"_%#", [[NSString stringWithCharacters:&c length:1] lowercaseString]];
} else {
[output appendFormat:#"%C", c];
}
}
return output;
}
NSString *MyUnderscoresToCamelCase(NSString *underscores) {
NSMutableString *output = [NSMutableString string];
BOOL makeNextCharacterUpperCase = NO;
for (NSInteger idx = 0; idx < [underscores length]; idx += 1) {
unichar c = [underscores characterAtIndex:idx];
if (c == '_') {
makeNextCharacterUpperCase = YES;
} else if (makeNextCharacterUpperCase) {
[output appendString:[[NSString stringWithCharacters:&c length:1] uppercaseString]];
makeNextCharacterUpperCase = NO;
} else {
[output appendFormat:#"%C", c];
}
}
return output;
}
Some drawbacks are that they do use temporary strings to convert between upper and lower case, and they don't have any logic for acronyms, so myURL will result in my_u_r_l.
Try this magic:
NSString* camelCaseString = #"myBundleVersion";
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"(?<=[a-z])([A-Z])|([A-Z])(?=[a-z])" options:0 error:nil];
NSString *underscoreString = [[regex stringByReplacingMatchesInString:camelCaseString options:0 range:NSMakeRange(0, camelCaseString.length) withTemplate:#"_$1$2"] lowercaseString];
NSLog(#"%#", underscoreString);
Output: my_bundle_version
If your concern is just the visibility of your code, you could make a category for NSString using the methods you've designed already. That way, you only see the ugly mess once. ;)
For instance:
#interface NSString(Conversions) {
- (NSString *)asCamelCase;
- (NSString *)asUnderscored;
}
#implementation NSString(Conversions) {
- (NSString *)asCamelCase {
// whatever you came up with
}
- (NSString *)asUnderscored {
// whatever you came up with
}
}
EDIT: After a quick Google search, I couldn't find any way of doing this, even in plain C. However, I did find a framework that could be useful. It's called RegexKitLite. It uses the built-in ICU library, so it only adds about 20K to the final binary.
Here's my implementation of Rob's answer:
#implementation NSString (CamelCaseConversion)
// Convert a camel case string into a dased word sparated string.
// In case of scanning error, return nil.
// Camel case string must not start with a capital.
- (NSString *)fromCamelCaseToDashed {
NSScanner *scanner = [NSScanner scannerWithString:self];
scanner.caseSensitive = YES;
NSString *builder = [NSString string];
NSString *buffer = nil;
NSUInteger lastScanLocation = 0;
while ([scanner isAtEnd] == NO) {
if ([scanner scanCharactersFromSet:[NSCharacterSet lowercaseLetterCharacterSet] intoString:&buffer]) {
builder = [builder stringByAppendingString:buffer];
if ([scanner scanCharactersFromSet:[NSCharacterSet uppercaseLetterCharacterSet] intoString:&buffer]) {
builder = [builder stringByAppendingString:#"-"];
builder = [builder stringByAppendingString:[buffer lowercaseString]];
}
}
// If the scanner location has not moved, there's a problem somewhere.
if (lastScanLocation == scanner.scanLocation) return nil;
lastScanLocation = scanner.scanLocation;
}
return builder;
}
#end
Here's yet another version based on all the above. This version handles additional forms. In particular, tested with the following:
camelCase => camel_case
camelCaseWord => camel_case_word
camelURL => camel_url
camelURLCase => camel_url_case
CamelCase => camel_case
Here goes
- (NSString *)fromCamelCaseToDashed3 {
NSMutableString *output = [NSMutableString string];
NSCharacterSet *uppercase = [NSCharacterSet uppercaseLetterCharacterSet];
BOOL previousCharacterWasUppercase = FALSE;
BOOL currentCharacterIsUppercase = FALSE;
unichar currentChar = 0;
unichar previousChar = 0;
for (NSInteger idx = 0; idx < [self length]; idx += 1) {
previousChar = currentChar;
currentChar = [self characterAtIndex:idx];
previousCharacterWasUppercase = currentCharacterIsUppercase;
currentCharacterIsUppercase = [uppercase characterIsMember:currentChar];
if (!previousCharacterWasUppercase && currentCharacterIsUppercase && idx > 0) {
// insert an _ between the characters
[output appendString:#"_"];
} else if (previousCharacterWasUppercase && !currentCharacterIsUppercase) {
// insert an _ before the previous character
// insert an _ before the last character in the string
if ([output length] > 1) {
unichar charTwoBack = [output characterAtIndex:[output length]-2];
if (charTwoBack != '_') {
[output insertString:#"_" atIndex:[output length]-1];
}
}
}
// Append the current character lowercase
[output appendString:[[NSString stringWithCharacters:&currentChar length:1] lowercaseString]];
}
return output;
}
If you are concerned with the speed of your code you probably want to write a more performant version of the code:
- (nonnull NSString *)camelCaseToSnakeCaseString {
if ([self length] == 0) {
return #"";
}
NSMutableString *output = [NSMutableString string];
NSCharacterSet *digitSet = [NSCharacterSet decimalDigitCharacterSet];
NSCharacterSet *uppercaseSet = [NSCharacterSet uppercaseLetterCharacterSet];
NSCharacterSet *lowercaseSet = [NSCharacterSet lowercaseLetterCharacterSet];
for (NSInteger idx = 0; idx < [self length]; idx += 1) {
unichar c = [self characterAtIndex:idx];
// if it's the last one then just append lowercase of character
if (idx == [self length] - 1) {
if ([uppercaseSet characterIsMember:c]) {
[output appendFormat:#"%#", [[NSString stringWithCharacters:&c length:1] lowercaseString]];
}
else {
[output appendFormat:#"%C", c];
}
continue;
}
unichar nextC = [self characterAtIndex:(idx+1)];
// this logic finds the boundaries between lowercase/uppercase/digits and lets the string be split accordingly.
if ([lowercaseSet characterIsMember:c] && [uppercaseSet characterIsMember:nextC]) {
[output appendFormat:#"%#_", [[NSString stringWithCharacters:&c length:1] lowercaseString]];
}
else if ([lowercaseSet characterIsMember:c] && [digitSet characterIsMember:nextC]) {
[output appendFormat:#"%#_", [[NSString stringWithCharacters:&c length:1] lowercaseString]];
}
else if ([digitSet characterIsMember:c] && [uppercaseSet characterIsMember:nextC]) {
[output appendFormat:#"%#_", [[NSString stringWithCharacters:&c length:1] lowercaseString]];
}
else {
// Append lowercase of character
if ([uppercaseSet characterIsMember:c]) {
[output appendFormat:#"%#", [[NSString stringWithCharacters:&c length:1] lowercaseString]];
}
else {
[output appendFormat:#"%C", c];
}
}
}
return output;
}
I have combined the answers found here into my refactoring library, es_ios_utils. See NSCategories.h:
#property(nonatomic, readonly) NSString *asCamelCaseFromUnderscores;
#property(nonatomic, readonly) NSString *asUnderscoresFromCamelCase;
Usage:
#"my_string".asCamelCaseFromUnderscores
yields #"myString"
Please push improvements!
I happened upon this question looking for a way to convert Camel Case to a spaced, user displayable string. Here is my solution which worked better than replacing #"_" with #" "
- (NSString *)fromCamelCaseToSpaced:(NSString*)input {
NSCharacterSet* lower = [NSCharacterSet lowercaseLetterCharacterSet];
NSCharacterSet* upper = [NSCharacterSet uppercaseLetterCharacterSet];
for (int i = 1; i < input.length; i++) {
if ([upper characterIsMember:[input characterAtIndex:i]] &&
[lower characterIsMember:[input characterAtIndex:i-1]])
{
NSString* soFar = [input substringToIndex:i];
NSString* left = [input substringFromIndex:i];
return [NSString stringWithFormat:#"%# %#", soFar, [self fromCamelCaseToSpaced:left]];
}
}
return input;
}
OK guys. Here is an all regex answer, which I consider the only true way:
Given:
NSString *MYSTRING = "foo_bar";
NSRegularExpression *_toCamelCase = [NSRegularExpression
regularExpressionWithPattern:#"(_)([a-z])"
options:NSRegularExpressionCaseInsensitive error:&error];
NSString *camelCaseAttribute = [_toCamelCase
stringByReplacingMatchesInString:MYSTRING options:0
range:NSMakeRange(0, attribute.length)
withTemplate:#"\\U$2"];
Yields fooBar.
Conversely:
NSString *MYSTRING = "fooBar";
NSRegularExpression *camelCaseTo_ = [NSRegularExpression
regularExpressionWithPattern:#"([A-Z])"
options:0 error:&error];
NSString *underscoreParsedAttribute = [camelCaseTo_
stringByReplacingMatchesInString:MYSTRING
options:0 range:NSMakeRange(0, attribute.length)
withTemplate:#"_$1"];
underscoreParsedAttribute = [underscoreParsedAttribute lowercaseString];
Yields: foo_bar.
\U$2 replaces second capture group with upper-case version of itself :D
\L$1 however, oddly, does not replace the first capture group with a lower-case version of itself :( Not sure why, it should work. :/