Weird cocoa bug? - objective-c

Hey folks, beneath is a piece of code i used for a school assignment.
Whenever I enter a word, with an O in it (which is a capital o), it fails!
Whenever there is one or more capital O's in this program, it returns false and logs : sentence not a palindrome.
A palindrome, for the people that dont know what a palindrome is, is a word that is the same read left from right, and backwards. (e.g. lol, kayak, reviver etc)
I found this bug when trying to check the 'oldest' palindrome ever found: SATOR AREPO TENET OPERA ROTAS.
When I change all the capital o's to lowercase o's, it works, and returns true.
Let me state clearly, with this piece of code ALL sentences/words with capital O's return false. A single capital o is enough to fail this program.
-(BOOL)testForPalindrome:(NSString *)s position:(NSInteger)pos {
NSString *string = s;
NSInteger position = pos;
NSInteger stringLength = [string length];
NSString *charOne = [string substringFromIndex:position];
charOne = [charOne substringToIndex:1];
NSString *charTwo = [string substringFromIndex:(stringLength - 1 - position)];
charTwo = [charTwo substringToIndex:1];
if(position > (stringLength / 2)) {
NSString *printableString = [NSString stringWithFormat:#"De following word or sentence is a palindrome: \n\n%#", string];
NSLog(#"%# is a palindrome.", string);
[textField setStringValue:printableString];
return YES;
}
if(charOne != charTwo) {
NSLog(#"%#, %#", charOne, charTwo);
NSLog(#"%i", position);
NSLog(#"%# is not a palindrome.", string);
return NO;
}
return [self testForPalindrome:string position:position+1];
}
So, is this some weird bug in Cocoa?
Or am I missing something?
B

This of course is not a bug in Cocoa, as you probably knew deep down inside.
Your compare method is causing this 'bug in Cocoa', you're comparing the addresses of charOne and charTwo. Instead you should compare the contents of the string with the isEqualToString message.
Use:
if(![charOne isEqualToString:charTwo]) {
Instead of:
if(charOne != charTwo) {
Edit: tested it in a test project and can confirm this is the problem.

Don't use charOne != charTwo
Instead use one of the NSString Compare Methods.
if ([charOne caseInsensitiveCompare:charTwo] != NSOrderedSame)
It may also have to do with localization (but I doubt it).

Related

Comparing string to a character of another string?

Here's my program so far. My intention is to have it so the if statement compares the letter in the string letterGuessed to a character in the string userInputPhraseString. Here's what I have. While coding in xCode, I get an "expected '['"error. I have no idea why.
NSString *letterGuessed = userInputGuessedLetter.text;
NSString *userInputPhraseString = userInputPhraseString.text;
int loopCounter = 0;
int stringLength = userInputPhraseString.length;
while (loopCounter < stringLength){
if (guessedLetter isEqualToString:[userInputPhraseString characterAtIndex:loopIndexTwo])
{
//if statement true
}
loopCounter++;
}
You are missing enclosing square brackets on this line:
if (guessedLetter isEqualToString:[userInputPhraseString characterAtIndex:loopIndexTwo])
It should be:
if ([guessedLetter isEqualToString:[userInputPhraseString characterAtIndex:loopIndexTwo]])
Edit that won’t fix your problem, though, because characterAtIndex: returns a unichar, not an NSString.
It's not clear what you are trying to do.. But I suppose that letterGuessed has one character... And that userInputPhraseString has many characters. So you want to know if letterGuessed is inside userInputPhraseString correct?
This is one solution without loops involved.. I replaced the input with fixed values for testing and tested the code.. It works.
NSString *letterGuessed = #"A"; //Change to your inputs
NSString *userInputPhraseString = #"BBBA"; //Since it has A it will be true in the test
NSCharacterSet *cset = [NSCharacterSet characterSetWithCharactersInString:letterGuessed];
NSRange range = [userInputPhraseString rangeOfCharacterFromSet:cset];
if (range.location != NSNotFound) { //Does letterGuessed is in UserInputPhraseString?
NSLog(#"YES"); //userInput Does contain A...
} else {
NSLog(#"NO");
}
In regards to your code... I fixed a couple of errors, first you are trying to get a UniChar (Integer) value for the character and want to compare it to a NSString which is an Object. Also fixed a couple of issues with syntax you had and used the right approach which is to return a range of characters. Again for doing what you want to accomplish the example above is the best approach I know, but for the sake of learning, here is your code fixed.
NSString *letterGuessed = #"A"; //Change to your inputs
NSString *userInputPhraseString = #"BBBA"; //Since it has A it will be true in the test
NSInteger loopCounter = 0; //Use NSInteger instead of int.
NSInteger stringLength = userInputPhraseString.length;
BOOL foundChar = NO; //Just for the sake of returning NOT FOUND in NSLOG
while (loopCounter < stringLength){
//Here we will get a letter for each iteration.
NSString *scannedLetter = [userInputPhraseString substringWithRange:NSMakeRange(loopCounter, 1)]; // Removed loopCounterTwo
if ([scannedLetter isEqualToString:letterGuessed])
{
NSLog(#"FOUND CHARACTER");
foundChar = YES;
}
loopCounter++;
}
if (!foundChar) NSLog(#"NOT FOUND");
NSRange holds the position, length.. So we move to a new position on every iteration and then get 1 character.
Also if this approach is what you want, I would strongly suggest a for-loop.

CS193p-Adding backspace to a calculator

I recently started following the online course on iPhone development from Stanford University on iTunes U.
I'm trying to do the homework assignments now for the first couple of lectures. I followed through the walkthrough where I built a basic calculator, but now I'm trying the first assignment and I can't seem to work it out. It's a follows:
Implement a “backspace” button for the user to press if they hit the wrong digit button. This is not intended to be “undo,” so if they hit
the wrong operation button, they are out of luck! It’s up to you to
decided how to handle the case where they backspace away the entire
number they are in the middle of entering, but having the display go
completely blank is probably not very user-friendly.
I followed this: Creating backspace on iOS calculator
So the code is
-(IBAction)backspacePressed:(UIButton *)sender {
NSMutableString *string = (NSMutableString*)[display text];
int length = [string length];
NSString *temp = [string substringToIndex:length-1];
[display setText:[NSString stringWithFormat:#"%#",temp]];
}
My question is shouldn't I check whether my last is a digit or operand? If operand, no execution and if digit, remove it...
First of all, there are several unnecessary steps in that code... And to answer your question, yes, you should check for an operand. Here is how I would write that method with a check:
NSString *text = [display text];
int length = [text length];
unichar c = [text characterAtIndex:length];
NSCharacterSet *digits = [NSCharacterSet decimalCharacterSet];
if ([digits characterIsMember:c] || c == '.') {
NSString *temp = [[display text] substringToIndex:length-1];
[display setText:temp];
}
I'm also going through the fall 2011 class on iTunes U.
The walk through gives us an instance variable userIsInTheMiddleOfEnteringANumber so I just checked to see if that is YES.
- (IBAction)backspacePressed {
if (self.userIsInTheMiddleOfEnteringANumber) {
if ([self.display.text length] > 1) {
self.display.text = [self.display.text substringToIndex:[self.display.text length] - 1];
} else {
self.display.text = #"0";
self.userIsInTheMiddleOfEnteringANumber = NO;
}
}
}
I used the approach taken by Joe_Schmoe, which is straightforward. (just remove characters in the dispaly until you reach the end).
If the user continues pressing 'Clear Error', I removed an item from the stack as well.
I have just started on the course myself, this post is quite old now but my solution might help others, food for thought if nothing else:
- (IBAction)deletePressed:(id)sender
{
NSString *displayText = [display text];
int length = [displayText length];
if (length != 1) {
NSString *newDisplayText = [displayText substringToIndex:length-1];
[display setText:[NSString stringWithFormat:#"%#",newDisplayText]];
} else {
userIsInTheMiddleOfEnteringANumber = NO;
NSString *newDisplayText = #"0";
[display setText:[NSString stringWithFormat:#"%#",newDisplayText]];
}
}

How to convert text to camel case in Objective-C?

I'm making little utility to help me generate code for an app I'm making. I like to have constants for my NSUserDefaults settings, so that my code is more readable and easier to maintain. The problem is, that making constants for everything takes some time, so I'm trying to write a utility to generate code for me. I'd like to be able to enter a string and have it converted to camel case, like so:
- (NSString *)camelCaseFromString:(NSString *)input{
return inputAsCamelCase;
}
Now, the input string might be composed of multiple words. I'm assuming that I need some sort of regular expression here, or perhaps there is another way to do it. I'd like to input something like this:
#"scrolling direction"
or this:
#"speed of scrolling"
and get back something like this:
kScrollingDirection
or this:
kSpeedOfScrolling
How would you go about removing spaces and replacing the character following the space with the uppercase version?
- (NSString *)camelCaseFromString:(NSString *)input {
return [#"k" stringByAppendingString:[[input capitalizedString] stringByReplacingOccurrencesOfString:#" " withString:#""]];
}
Capitalize each word.
Remove whitespace.
Insert "k" at the beginning. (Not literally, but a simplification using stringByAppendingString.)
The currently accepted answer has a bug. (also pointed out by #jaydee3)
Words already having proper camelCasing, PascalCasing, or capitalized TLA acronyms will have their correct casing "destroyed" by having non-first characters lowercased via the call to capitalizedString.
So "I prefer camelCasing to PascalCasing for variables" would look like "iPreferCamelcasingToPascalcasingForVariables" but according to the question, should be "iPreferCamelCasingToPascalCasingForVariables"
Use the below category on NSString to create non-destructive camel and pascal casing. It also properly leaves ALL_CAPS words/acronyms in place, although that wasn't really part of the orig question.
An acronym as a first word for a camelCased string would be weird. "WTH" becomes "wTH" which looks weird. Anyway, that's an edge case, and not addressed. However, since question asker is prefixing with "k" then it "k IBM" becomes "kIBM" which looks ok to me, but would look be "kIbm" with currently accepted answer.
Use:
NSString *str = #"K computer manufacturer IBM";
NSLog(#"constant: %#", str.pascalCased);
// "constant: kComputerManufacturerIBM"
Category (class extension) code.
#implementation NSString (MixedCasing)
- (NSString *)camelCased {
NSMutableString *result = [NSMutableString new];
NSArray *words = [self componentsSeparatedByString: #" "];
for (uint i = 0; i < words.count; i++) {
if (i==0) {
[result appendString:((NSString *) words[i]).withLowercasedFirstChar];
}
else {
[result appendString:((NSString *)words[i]).withUppercasedFirstChar];
}
}
return result;
}
- (NSString *)pascalCased {
NSMutableString *result = [NSMutableString new];
NSArray *words = [self componentsSeparatedByString: #" "];
for (NSString *word in words) {
[result appendString:word.withUppercasedFirstChar];
}
return result;
}
- (NSString *)withUppercasedFirstChar {
if (self.length <= 1) {
return self.uppercaseString;
} else {
return [NSString stringWithFormat:#"%#%#",[[self substringToIndex:1] uppercaseString],[self substringFromIndex:1]];
}
}
- (NSString *)withLowercasedFirstChar {
if (self.length <= 1) {
return self.lowercaseString;
} else {
return [NSString stringWithFormat:#"%#%#",[[self substringToIndex:1] lowercaseString],[self substringFromIndex:1]];
}
}
#end
Just use: #"This is a sentence".capitalizedString;
Becomes: >> "This Is A Sentence"
Replace spaces and manipulate...
Uppercases first char of word, and lowers the other letters for each word.

NSString search for substring/ csv File IO

This has given me quite a big headache. For whatever reason, when I use this code, the if statement always evaluates to false:
while(!feof(file))
{
NSString *line = [self readNSString:file];
NSLog(#"%#", line);
NSLog(#"%#", search);
NSRange textRange;
textRange =[line rangeOfString:search];
if(textRange.location != NSNotFound)
{
NSString *result = [line substringFromIndex:NSMaxRange([line rangeOfString:search])];
resultView.text = result;
}
else
{
resultView.text = #"Not found";
}
}
When the functions execute, the two NSLogs tell me that the "line" and "search" strings are what they should be, so then why does the if statement always evaluate to false? I must be missing something simple, having another set of eyes would be great. Thanks
edit: (function "readNSString")
- (NSString*)readNSString:(FILE*) file
{
char buffer[300];
NSMutableString *result = [NSMutableString stringWithCapacity:256];
int read;
do
{
if(fscanf(file, "%299[^\n]%n%*c", buffer, &read) == 1)
[result appendFormat:#"%s", buffer];
else
break;
} while(r == 299);
return result;
}
edit 2:
search is set with a call to the first function, with an NSString* variable as a parameter, like this:
NSString *textFieldText = [[NSString alloc]
initWithFormat:#"%#", textField.text];
[self readFile:textFieldText];
edit 3 (NSLogs output)
line: Germany Italy France
search: Italy
I think that you are using the rangeOfString and the NSNotFound etc. correctly, so the problem is possibly to do with the creation of the string from the data read from the file using the appendFormat:#"%s".
I suspect there may be an encoding issue between your two string formats - I would investigate whether the "%s" encodes the null terminated C string properly into the same format as a unicode NSString with the appropriate encoding.
Try hard coding the value you are getting from the readNSString function as a string literal in code just for testing and see if that comparison works, if so this would tend to indicate it probably is something to do with the encoding of the string created from the file.

Is there a way to get Spell Check data from an NSString?

I'm writing a simple shift cipher iPhone app as a pet project, and one piece of functionality I'm currently designing is a "universal" decryption of an NSString, that returns an NSArray, all of NSStrings:
- (NSArray*) decryptString: (NSString*)ciphertext{
NSMutableArray* theDecryptions = [NSMutableArray arrayWithCapacity:ALPHABET];
for (int i = 0; i < ALPHABET; ++i) {
NSString* theNewPlainText = [self decryptString:ciphertext ForShift:i];
[theDecryptions insertObject:theNewPlainText
atIndex:i];
}
return theDecryptions;
}
I'd really like to pass this NSArray into another method that attempts to spell check each individual string within the array, and builds a new array that puts the strings with the fewest typo'd words at lower indicies, so they're displayed first. I'd like to use the system's dictionary like a text field would, so I can match against words that have been trained into the phone by its user.
My current guess is to split a given string up into words, then spell check each with NSSpellChecker's -checkSpellingOfString:StartingAt: and using the number of correct words to sort the Array. Is there an existing library method or well-accepted pattern that would help return such a value for a given string?
Well, I found a solution that works using UIKit/UITextChecker. It correctly finds the user's most preferred language dictionary, but I'm not sure if it includes learned words in the actual rangeOfMisspelledWords... method. If it doesn't, calling [UITextChecker hasLearnedWord] on currentWord inside the bottom if statement should be enough to find user-taught words.
As noted in the comments, it may be prudent to call rangeOfMisspelledWords with each of the top few languages in [UITextChecker availableLanguages], to help multilingual users.
-(void) checkForDefinedWords {
NSArray* words = [message componentsSeparatedByString:#" "];
NSInteger wordsFound = 0;
UITextChecker* checker = [[UITextChecker alloc] init];
//get the first language in the checker's memory- this is the user's
//preferred language.
//TODO: May want to search with every language (or top few) in the array
NSString* preferredLang = [[UITextChecker availableLanguages] objectAtIndex:0];
//for each word in the array, determine whether it is a valid word
for(NSString* currentWord in words){
NSRange range;
range = [checker rangeOfMisspelledWordInString:currentWord
range:NSMakeRange(0, [currentWord length])
startingAt:0
wrap:NO
language:preferredLang];
//if it is valid (no errors found), increment wordsFound
if (range.location == NSNotFound) {
//NSLog(#"%# %#", #"Valid Word found:", currentWord);
wordsFound++;
}
else {
//NSLog(#"%# %#", #"Invalid Word found:", currentWord);
}
}
//After all "words" have been searched, save wordsFound to validWordCount
[self setValidWordCount:wordsFound];
[checker release];
}