convert string to URL objective-C - objective-c

I am creating a screen where user can input any text. Now that text has to be converted to a valid url. I have looked into lot of stack overflow question and they have come up with the fooling solution:
1. CFURLCreateStringByAddingPercentEscapes
NSString *encodedString = (NSString *)CFURLCreateStringByAddingPercentEscapes(
NULL,
(CFStringRef)unencodedString,
NULL,
(CFStringRef)#"!*'();:#&=+$,/?%#[]",
kCFStringEncodingUTF8 );
2. Category on the string
- (NSString *)urlencode {
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;
}
Example: If the user manually puts the required %20 (for example) in the text instead of white space and then if we use any of the above solution the %20 would be converting to %25.
Could anybody let me know how can I fix the issue.

Because % will be urlencoded to %25, actually the %20 will be urlencoded to %2520, which is the desired output.
If you want to keep the %20 in the user input, you can replace all the %20 with a single white space, then urlencoded the replaced string.

Related

How to unescape backslashes on macOS?

Localized.strings files may contain escaped chars like \n and \".
How do I efficiently unescape them?
I know I could write my own function that looks for a \ and removes it and then keeps searching at the second-next character (so that I don't turn \\ into nothing), but that won't handle special escape methods such as an octal character numbers (e.g. \012 for a LF) and possibly other forms.
I'd think that NSString would offer a function for that but I can't find any.
Actually, this appears to be a duplicate of NSString strip regular \ escape characters how?, but that question has a lot of bad answers and not a single correct answer. And the user is not active any more, meaning no one will ever accept a correct answer there. How shall that be handled on SO?
Here's a self-written ObjC version that works for most cases. It defines an unescaped method for NSString.
#import <Cocoa/Cocoa.h>
#interface NSString (BackslashUnescaping)
- (NSString*) unescaped;
#end
#implementation NSString (BackslashUnescaping)
- (NSString*) unescaped
{
NSString *text = self;
NSInteger charIndex = 0;
while (true) {
NSInteger textLen = text.length;
if (charIndex >= textLen) {
break;
}
NSRange remainingRange = NSMakeRange (charIndex, textLen-charIndex);
NSRange charRange = [text rangeOfString:#"\\"options:0 range:remainingRange];
if (charRange.length == 0) {
// no more backslashes -> done
break;
}
charIndex = charRange.location + 1;
if (charIndex >= textLen) {
// reached end of string -> exit loop
break;
}
// check char following the backslash
unichar nextChar = [text characterAtIndex:charIndex];
unichar replacementChar;
NSInteger skipLen = 1;
if (nextChar >= 'a' && nextChar <= 'z') {
if (nextChar == 'n') {
replacementChar = '\n'; // LF
} else if (nextChar == 'r') {
replacementChar = '\r'; // CR
} else if (nextChar == 't') {
replacementChar = '\t'; // TAB
} else if (nextChar == 'x') {
// A hex char code
const NSInteger xtraLen = 2;
if (charIndex+xtraLen >= textLen) break;
// Note: Does not make sure that both chars are valid hex chars
NSString *code = [text substringWithRange:NSMakeRange(charIndex+1, 2)];
char ch = strtol(code.UTF8String, NULL, 16);
replacementChar = ch;
skipLen += xtraLen;
} else if (nextChar == 'u') {
// A unicode char code
const NSInteger xtraLen = 4;
if (charIndex+xtraLen >= textLen) break;
// Note: Does not make sure that all four chars are valid hex chars
NSString *code = [text substringWithRange:NSMakeRange(charIndex+1, 4)];
unichar ch = strtol(code.UTF8String, NULL, 16);
replacementChar = ch;
skipLen += xtraLen;
} else {
// an unknown escape code - this should be fixed
NSAssert(false, #"There's a case missing for escaping \\%c", nextChar);
}
} else if (nextChar >= '0' && nextChar <= '9') {
unichar nextChar2 = 0;
if (charIndex > textLen) { // get the second octal char
nextChar2 = [text characterAtIndex:charIndex+1];
}
if (nextChar == '0' && (nextChar2 < '0' || nextChar2 > '9')) {
// A short NUL (\0) char
replacementChar = 0;
} else {
// An octal char code
const NSInteger xtraLen = 2;
if (charIndex+xtraLen >= textLen) break;
// Note: Does not make sure that the last char is a valid octal char
NSString *code = [text substringWithRange:NSMakeRange(charIndex, 3)];
char ch = strtol(code.UTF8String, NULL, 8); // https://stackoverflow.com/a/12820646/43615
replacementChar = ch;
skipLen += xtraLen;
}
} else {
// Handle all generic escapes, like for \\ and \"
replacementChar = nextChar;
}
#if 0 // Use string concatenation
charIndex += skipLen-1;
NSString *head = [text substringToIndex:charRange.location];
NSString *tail = [text substringFromIndex:charIndex+1];
text = [NSString stringWithFormat:#"%#%C%#", head, replacementChar, tail];
#else // Use a mutable string
if (text == self) {
text = text.mutableCopy;
}
NSRange replacedRange = NSMakeRange(charRange.location, skipLen+1);
NSString *replacement = [NSString stringWithCharacters:&replacementChar length:1];
[(NSMutableString*)text replaceCharactersInRange:replacedRange withString:replacement];
charIndex += 1;
#endif
}
return text;
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSArray *testValues = #[
#"CR:\\rLF:\\n",
#"\\\"quoted\\\"",
#"Backslash: \\\\",
#"Octal x (\170):\\170",
#"Hex x (\x78):\\x78",
#"Unicode ะค (\u0424):\\u0424",
#"NUL char:\\0.",
#"Bad Hex:\\x7x", // this is not detected being invalid
#"Bad Hex:\\x7",
#"Incomplete :\\13"
];
for (NSString *s in testValues) {
NSString *s2 = [NSString stringWithFormat:#"Escaped: %#\nUnescaped: %#", s, s.unescaped];
printf("\n%s\n", s2.UTF8String);
}
}
return 0;
}

How to select sqlite order by char and number key?

Some value of col in sqlite database:
a1b2
a2b3
a1b10
a10b1
if use order by in sql it will be:
a10b1
a1b10
a1b2
a2b3
I want it like NSNumericSearch of objc like this:
a1b2
a1b10
a2b3
a10b1
How do I write the SQL statements?
Start by creating a custom collating function:
int compare_natural(void *data, int len1, const void *str1, int len2, const void *str2) {
if (str1 && len1 > 0 && str2 && len2 > 0) {
NSString *s1 = [[NSString alloc] initWithBytesNoCopy:(void *)str1 length:len1 encoding:NSUTF8StringEncoding freeWhenDone:NO];
NSString *s2 = [[NSString alloc] initWithBytesNoCopy:(void *)str2 length:len2 encoding:NSUTF8StringEncoding freeWhenDone:NO];
// The use of NSNumericSearch is required for your need.
// The others are options depending on your needs
NSComparisonResult res = [s1 compare:s2 options:NSCaseInsensitiveSearch | NSNumericSearch | NSDiacriticInsensitiveSearch];
return res;
} else if (str1 && len1 > 0 && (!str2 || len2 == 0)) {
return -1; // empty strings to the end of the list (optional)
} else if (str2 && len2 > 0 && (!str1 || len1 == 0)) {
return 1; // empty strings to the end of the list (optional)
} else {
return 0;
}
}
Then register the custom collator. This needs to be done after you open the database.
// dbRef is your sqlite3 database reference
int rc = sqlite3_create_collation(dbRef, "BYNUMBER", SQLITE_UTF8, NULL, &compare_natural);
Then update your query so it ends with "COLLATE BYNUMBER"
SELECT some_col ORDER BY some_col COLLATE BYNUMBER;

Definition error while using instance of class that is in an if loop

----I have two instances of NSString, and one of them is defined within a while loop, but the other one is after that. Xcode seems to think that since this first instance (we'll call string1) is in a while loop, it will not be defined. However, for the program to proceed out of the while loop IT WILL ALWAYS DEFINE STRING1. An NSString is in another while loop thats the same thing.
----Outside of both while loops, at the end, in the code I have a method of NSString done to both of them (isEqualtoString), but Xcode tells me that string1 and string two are not defined. The program should work, but the compiler stops me. Is there anything I can change to make string1 and string2 appear defined in Xcode's eyes.
----I'm using this for the registration page, and I need these in while loops because they need to cycle until the user enters in through the console a username that fits my requirements.
EDIT: Added in actual code.
int numb1, numb2;
char usercheck1[60];
char usercheck2[60];
//Registration
numb2 = 1;
while (numb2 == 1){
numb1 = 1;
while (numb1 == 1){
numb1 = 0;
NSLog(#"Username:");
fgets(usercheck1, sizeof usercheck1, stdin);
int c2;
while((c2 = getchar()) != '\n' && c2 != EOF);
if (usercheck1 [sizeof (usercheck1)-1] == '\n'){ // In case that the input string has 12 characters plus '\n'
usercheck1 [sizeof (usercheck1)-1] = '\0';} // Plus '\0', the '\n' isn't added and the if condition is false.
NSString* string1 = [NSString stringWithUTF8String: usercheck1];
//Makes sure string contains no spaces and string length is correct size.
if ([string1 length] > 12){
NSLog (#"Username must be 12 characters or less!");
numb1 = 1;}
if ([string1 length] < 5){
NSLog (#"Username must be 4 characters or more!");
numb1 = 1;}
if ([string1 rangeOfString:#" " ].location != NSNotFound){
NSLog(#"Username cannot contain spaces!");
numb1 = 1;}
}
numb1 = 1;
while (numb1 == 1){
numb1 = 0;
NSLog(#"Confirm Username:");
fgets(usercheck2, sizeof usercheck2, stdin);
int c2;
while((c2 = getchar()) != '\n' && c2 != EOF);
if (usercheck2 [sizeof (usercheck2)-1] == '\n'){ // In case that the input string has 12 characters plus '\n'
usercheck2 [sizeof (usercheck2)-1] = '\0';} // Plus '\0', the '\n' isn't added and the if condition is false.
NSString* string2 = [NSString stringWithUTF8String: usercheck2];
//Makes sure string contains no spaces and string length is correct size.
if ([string2 length] > 12){
NSLog (#"Username must be 12 characters or less!");
numb1 = 1;}
if ([string2 length] < 5){
NSLog (#"Username must be 4 characters or more!");
numb1 = 1;}
if ([string2 rangeOfString:#" " ].location != NSNotFound){
NSLog(#"Username cannot contain spaces!");
numb1 = 1;}
}
if ([string2 isEqualToString: string1] == YES){
NSLog(#"Usernames confirmed! Username:%s", string2);
numb2 = 0;}
else {NSLog(#"Usernames do not match. Try again");
numb2 = 1;}
}
}
As you can see, it would work if it actually compiled and ran, but the compiler just doesn't like me using string2 in the if statement for isEqualToString. It gives me the error :
"Use of undeclared identifier 'string2'"
Also, move that statement and the else statment outside the two sub-while statements, it gives me that error for BOTH string1 and string2.
XCode version is 4.6.3, I'm programming for the Mac OS X on 10.8.4
You can't access variables outside of the scope in which they are declared. Since string1 and string2 are declared within the two while blocks, you can't use them outside of the while blocks.
There are many things that could be improved in this code. Try something like this:
NSString *username1;
NSString *username2;
while (1) {
while (1) {
NSLog(#"Username:");
char usercheck[60];
fgets(usercheck, sizeof usercheck1, stdin);
int c2;
while ((c2 = getchar()) != '\n' && c2 != EOF);
if (usercheck [sizeof (usercheck) - 1] == '\n') { // In case that the input string has 12 characters plus '\n'
usercheck[sizeof (usercheck)-1] = '\0';
} // Plus '\0', the '\n' isn't added and the if condition is false.
NSString *string1 = [NSString stringWithUTF8String:usercheck];
// Makes sure string contains no spaces and string length is correct size.
if ([string1 length] > 12) {
NSLog(#"Username must be 12 characters or less!");
} else if ([string1 length] < 5) {
NSLog(#"Username must be 4 characters or more!");
} else if ([string1 rangeOfString:#" "].location != NSNotFound) {
NSLog(#"Username cannot contain spaces!");
} else {
username1 = string1;
break; // username is good
}
}
while (1) {
NSLog(#"Confirm Username:");
char usercheck[60];
fgets(usercheck, sizeof usercheck, stdin);
int c2;
while ((c2 = getchar()) != '\n' && c2 != EOF);
if (usercheck[sizeof (usercheck) - 1] == '\n') { // In case that the input string has 12 characters plus '\n'
usercheck[sizeof (usercheck) - 1] = '\0';
} // Plus '\0', the '\n' isn't added and the if condition is false.
NSString *string2 = [NSString stringWithUTF8String:usercheck];
//Makes sure string contains no spaces and string length is correct size.
if ([string2 length] > 12) {
NSLog (#"Username must be 12 characters or less!");
} else if ([string2 length] < 5) {
NSLog (#"Username must be 4 characters or more!");
} else if ([string2 rangeOfString:#" "].location != NSNotFound) {
NSLog(#"Username cannot contain spaces!");
} else {
username2 = string2;
break;
}
}
if ([username1 isEqualToString:username2]) {
NSLog(#"Usernames confirmed! Username:%#", username1);
break;
} else {
NSLog(#"Usernames do not match. Try again");
}
}

Error while trying to access Google translate with NSURL

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.

How to check whether a char is digit or not in Objective-C?

I need to check if a char is digit or not.
NSString *strTest=#"Test55";
char c =[strTest characterAtIndex:4];
I need to find out if 'c' is a digit or not. How can I implement this check in Objective-C?
Note: The return value for characterAtIndex: is not a char, but a unichar. So casting like this can be dangerous...
An alternative code would be:
NSString *strTest = #"Test55";
unichar c = [strTest characterAtIndex:4];
NSCharacterSet *numericSet = [NSCharacterSet decimalDigitCharacterSet];
if ([numericSet characterIsMember:c]) {
NSLog(#"Congrats, it is a number...");
}
In standard C there is a function int isdigit( int ch ); defined in "ctype.h". It will return nonzero (TRUE) if ch is a digit.
Also you can check it manually:
if(c>='0' && c<='9')
{
//c is digit
}
There is a C function called isdigit.
This is actually quite simple:
isdigit([YOUR_STRING characterAtIndex:YOUR_POS])
You may want to check NSCharacterSet class reference.
You can think of writing a generic function like the following for this:
BOOL isNumericI(NSString *s)
{
NSUInteger len = [s length];
NSUInteger i;
BOOL status = NO;
for(i=0; i < len; i++)
{
unichar singlechar = [s characterAtIndex: i];
if ( (singlechar == ' ') && (!status) )
{
continue;
}
if ( ( singlechar == '+' ||
singlechar == '-' ) && (!status) ) { status=YES; continue; }
if ( ( singlechar >= '0' ) &&
( singlechar <= '9' ) )
{
status = YES;
} else {
return NO;
}
}
return (i == len) && status;
}