Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

iMasterWeb

macrumors regular
Original poster
Mar 15, 2009
158
0
Hi there, right now I'm working on a relatively simple program and I think I need a second pair of eyes on this because I can't see why its not working. Basically, the program is skipping the if part of the if statement, whether the statement is true or not, and going straight to the else statement. So here's my program (if your wondering, its a tweet shortener)

@interface:
Code:
#import <Foundation/Foundation.h>


@interface tweet : NSObject {

	BOOL shorten;	
	NSMutableArray *longWords;
	NSMutableArray *shortWords;
	NSString *reply;
	NSMutableString *shortTweet;
	NSMutableString *usrTweet;
}


-(void) getTweet;
-(void) checkLength;
-(void) initObjects;
-(void) shorten: (NSMutableString *) s;
-(void) findAndReplace: (NSMutableString *) f;

@end

@implementation
Code:
#import "tweet.h"
#import <Foundation/NSString.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSAutoreleasePool.h>
#import "stdio.h"

@implementation tweet

-(void) getTweet {
	char firstTweet[250];
	fgets(firstTweet, 250, stdin);
	if(firstTweet[strlen(firstTweet) - 1] == '\n') {
	   firstTweet[strlen(firstTweet) - 1] = 0;
	}
	usrTweet = [NSMutableString stringWithUTF8String: firstTweet];
	[self checkLength];
}

-(void) checkLength {
	if([usrTweet length] > 140) {    //***skipping this***
		int overflow = [usrTweet length] - 140;
		NSLog(@"Your tweet is %i characters long, %i more than 140. At this point, you can choose to shorten your tweet with our built in shortener or publish as is.", [usrTweet length], overflow);
		char responseC[20];
		fgets(responseC, 20, stdin);
		if(responseC[strlen(responseC) - 1] == '\n') {
			responseC[strlen(responseC) - 1] = 0;
		}
		reply = [NSString stringWithUTF8String: responseC];
		do{
			if (reply = @"publish") {
				shorten = NO;
				NSLog(@"Your tweet is: %@", usrTweet);
				return;
			} else if (reply = @"shorten") {
				shorten = YES;
				[self shorten: usrTweet];
			} else NSLog(@"Please enter either 'shorten' or 'publish' to continue");
			} while( shorten != YES || NO );
	} else {   //***doing this***
		NSLog(@"Your tweet is: %@", usrTweet);
		return;
	}
}

-(void) initObjects {
	longWords = [NSMutableArray arrayWithObjects: @"because", @"for" , @"two", @"to", @"some", @"with", @"without", nil];
	shortWords = [NSMutableArray arrayWithObjects: @"b/c", @"4", @"2", @"2", @"sum", @"w/", @"w/o", nil];
	shortTweet = [NSMutableString stringWithString: usrTweet];
}



-(void) shorten: (NSMutableString *) s {
	[self initObjects];
	[self findAndReplace: shortTweet];
	NSLog(@"Your shortened tweet is: %@", shortTweet);
}	
	
-(void) findAndReplace: (NSMutableString *) f {
	if([longWords count] != [shortWords count]) {
	NSLog(@"Error: Arrays unequal. Terminating"); 
	return;
}
	int amount = [longWords count];
	int i;
	for(i = 0; i < amount; ++i) {
		NSString *search = [NSString stringWithString: [longWords objectAtIndex: i]];
		NSString *replace = [NSString stringWithString: [shortWords objectAtIndex: i]];
		[shortTweet replaceOccurrencesOfString: search 
									withString: replace 
									   options: 0 
										 range: NSMakeRange (0, [shortTweet length])];
	}
}

@end

main:
Code:
#import <Foundation/Foundation.h>
#import "tweet.h"
#import <Foundation/NSString.h>
#import <Foundation/NSArray.h>

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
	tweet *myTweet = [[tweet alloc]init];
    [myTweet getTweet];
	[myTweet release];
	[pool drain];
    return 0;
}

I've looked at the Mac OS Dev Center (or whatever you call it) and the documentation there, and couldn't find anything that contradicts anything I'm doing here...so I'm not sure what the problem is. The program returns no errors or warnings, so I have no way of knowing what's wrong, although knowing myself its probably something obvious that I'm just missing. Thanks guys!! You are always so much help!
 
I've don't have any experience with ObjC string comparisons but I think you need something more along this line:

Code:
if ( NSOrderedSame == [reply compare:@"publish"] ) {
   shorten = NO;
   NSLog(@"Your tweet is: %@", usrTweet);
   return;
} else if ( NSOrderedSame == [reply compare:@"shorten"] ) {
 
I've don't have any experience with ObjC string comparisons but I think you need something more along this line:

Code:
if ( NSOrderedSame == [reply compare:@"publish"] ) {
   shorten = NO;
   NSLog(@"Your tweet is: %@", usrTweet);
   return;
} else if ( NSOrderedSame == [reply compare:@"shorten"] ) {

Thanks for the advice! Unfortunately, that didn't fix the problem, but I appreciate your trying to help!
 
The obvious question is, how do you know that your usrTweet isn't always less than 140 characters?
 
You haven't stated the input that is leading to your stated difficulty.

I can't duplicate you failure.

Line less than or equal to 140 characters get echo'd while line greater than 140 get reported.
 
What do you think happens if the contents of responseC is not a valid UTF-8 string?
 
Agree with the post above, you should verify that the creation of the usrTweet string didn't fail. To debug, try outputting the length & contents of usrTweet before calling checkLength.
 
Code:
		do{
			if (reply = @"publish") {
				shorten = NO;
				NSLog(@"Your tweet is: %@", usrTweet);
				return;
			} else if (reply = @"shorten") {
				shorten = YES;
				[self shorten: usrTweet];
			} else NSLog(@"Please enter either 'shorten' or 'publish' to continue");
			} while( shorten != YES || NO );

Do you mean '==' instead of '='?
 
Agree with the post above, you should verify that the creation of the usrTweet string didn't fail. To debug, try outputting the length & contents of usrTweet before calling checkLength.

I had tried that earlier to try to figure out if it was even registering and it wasn't. The output would resemble
Code:
Length:
Your tweet is:

or if I left it as a C string (something else that I tried)

Code:
Length: 0
Your tweet is: null

So I'm not quite sure what's going on... As for whether or not its a valid UTF8 string, as far as I know the tweet I am testing with is as it is just letters and spaces. Sorry for not including that in the first post. NOTE: The above results only displayed in the console when the tweet was MORE than 140 characters, it works perfectly when the tweet is below 140 characters. This is why I'm assuming its something within the part of the if statement for the tweets more than 140 characters...I'm just not sure what.

admanimal said:
The obvious question is, how do you know that your usrTweet isn't always less than 140 characters?
Because I know the length of what I'm typing (its about 150 characters)

And probably should be:

Code:
:
if ([reply isEqualToString: @"publish"])
{
    ...
}
Thanks!
 
I hope you realize that your if/else in checkLength is an if/ELSE. The else-block that prints the shortened output won't be executed at all if the if-block that contains do/while is executed.

Frankly, you're doing way too much in checkLength, and it's confusing you and making it impossible for you to think through what's happening. If you can't think it through, you can't understand it, and if you can't understand it, you can't fix it.

You should do one simple thing in each simple method, then call the simple methods in sequence. Don't do everything in one method or function. Break It Down.

http://www.cocoadev.com/index.pl?BreakItDown

For example, define one method for each sub-task: getting the initial string, getting the option to shorten or publish, shortening the current string, printing the current string. Notice that you've got very similar code written in two places that calls fgets() and then turns it into an NSString*. That's poor design; there should be one function to do that, called from two places.

Finally, the condition of your do/while loop:

Code:
while( shorten != YES || NO );

is not doing what you think it's doing. The first problem with it: a BOOL variable only has 2 legal values. YES or NO. There is no third value outside of YES or NO. Therefore, testing for "not equal to YES or NO" is bogus. The second problem is the expression actually evaluates as:

Code:
  ( (shorten != YES) || (NO) )
because of the precedence of the operators involved. The (NO) part of the expression will never be true (i.e. NO is always false). You should rethink what you're doing here.
 
Alrighty, I've broken it down as Chown suggested and I'll admit it seems like a much cleaner code. But that's about it, it doesn't work any better. I've concluded that one of two things are happening:

1) I am converting the strings incorrectly thus creating an invalid string.
2) There's something wrong with my method of comparing to see if the string is in fact longer than 140 characters.

Now my code is as follows:

@interface
Code:
#import <Foundation/Foundation.h>


@interface tweet : NSObject {

	BOOL shorten;
	
	NSMutableArray *longWords;
	NSMutableArray *shortWords;
	
	NSString *reply;
	NSString *usrTweet;
	NSMutableString *shortTweet;
	
	
	int overflow;
}


-(void) getTweet;
-(int) checkLength;
-(void) initObjects;
-(void) shorten;
-(BOOL) askToShorten;
-(void) print;
@end
@implementation
Code:
#import "tweet.h"
#import <Foundation/NSString.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSAutoreleasePool.h>
#import "stdio.h"

@implementation tweet

-(void) getTweet {
	char firstTweet[250];
	fgets(firstTweet, 250, stdin);
	if(firstTweet[strlen(firstTweet) - 1] == '\n') {
	   firstTweet[strlen(firstTweet) - 1] = 0;
	}
	usrTweet = [NSMutableString stringWithUTF8String: firstTweet];
	[self initObjects];
}

-(int) checkLength {
	if([shortTweet length] > 140) {
		int overflow = [shortTweet length] - 140;
	} else shorten = NO;
	return [shortTweet length];
}

-(BOOL) askToShorten {
		NSLog(@"Your tweet is %i characters long, %i more than 140. At this point, you can choose to shorten your tweet with our built in shortener or publish as is.", [usrTweet length], overflow);
		char responseC[20];
		fgets(responseC, 20, stdin);
		if(responseC[strlen(responseC) - 1] == '\n') {
			responseC[strlen(responseC) - 1] = 0;
		}
	reply = [NSString stringWithUTF8String: responseC];
	
	int valid = 0;
	do{
		if ([reply isEqualToString:@"publish"]) {
			shorten = NO;
			valid = 1;
		} else if ([reply isEqualToString:@"shorten"]) {
			shorten = YES;
			valid = 1;
		} else { 
			NSLog(@"Please enter either 'shorten' or 'publish' to continue");
		}
	} while (valid = 0);
	return shorten;
}
	
-(void) initObjects {
	longWords = [NSMutableArray arrayWithObjects: @"because", @"for" , @"two", @"to", @"some", @"with", @"without", nil];
	shortWords = [NSMutableArray arrayWithObjects: @"b/c", @"4", @"2", @"2", @"sum", @"w/", @"w/o", nil];
	shortTweet = [NSMutableString stringWithString: usrTweet];
}


-(void) shorten {
	int amount = [longWords count];
	int i;
	for(i = 0; i < amount; ++i) {
		NSString *search = [NSString stringWithString: [longWords objectAtIndex: i]];
		NSString *replace = [NSString stringWithString: [shortWords objectAtIndex: i]];
		[shortTweet replaceOccurrencesOfString: search 
									withString: replace 
									   options: 0 
										 range: NSMakeRange (0, [shortTweet length])];
	}
}
	
-(void) print {
	NSLog(@"Your tweet is: %@", shortTweet);
}

@end
main
Code:
#import <Foundation/Foundation.h>
#import "tweet.h"
#import <Foundation/NSString.h>
#import <Foundation/NSArray.h>

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
	tweet *myTweet = [[tweet alloc]init];
    [myTweet getTweet];
	if([myTweet checkLength] > 140) {
		NSLog(@"Tweet is longer than 140 characters"); //This line doesn't print 
		if([myTweet askToShorten] == YES) {
			[myTweet shorten];
			[myTweet print];
		} else [myTweet print];
	} else {
		[myTweet print];
	}
	[myTweet release];
	[pool drain];
    return 0;
}

So when the tweet is less than 140 characters, it works fine, no problems. If it is more than 140 characters, it SHOULD go into the if statement and print that NSLog statement, but it doesn't so that's where the problem lies. The thing that's stumping me is that it seems as if it couldn't be the converting of the string because strings under 140 characters work fine. I really appreciate all of your guys' help! Thanks a ton!

P.S. Chown - I haven't implemented my own method of converting the strings because that gave me some errors (something with the type I was setting for the c string) but I'll work it out eventually.

P.S.S And sorry about the whole
Code:
while( shorten != YES || NO );
problem....I just wasn't thinking it through there :p
 
Obvious problems only. Not a deep analysis.

Code:
-(int) checkLength {
	if([shortTweet length] > 140) {
		int overflow = [shortTweet length] - 140;
The local int 'overflow' is the same name as the instance variable. You are using the instance variable elsewhere, but it isn't being assigned a value.


Code:
	do{
		if ([reply isEqualToString:@"publish"]) {
			shorten = NO;
			valid = 1;
		} else if ([reply isEqualToString:@"shorten"]) {
			shorten = YES;
			valid = 1;
		} else { 
			NSLog(@"Please enter either 'shorten' or 'publish' to continue");
		}
	}

This loop doesn't re-ask the "publish or shorten" question.


Code:
while (valid = 0);
Wrong. You're using assignment (=) instead of testing for equality (==). The value of the expression will be the value assigned (0).
 
Fixed issues 1 and 3, careless mistakes.

As for your second issue. there's no reason to ask the question again because shortening it again won't do anything, therefore the only option is to publish, so I just do that for them.

The console still displays the same thing, though. I enter a tweet over 140 characters and I get the following:
Code:
Your tweet is:
That's it. Nothing else. The more I look at this program, the more and more stumped I become. If anyone has any other ideas ore tips, feel free to share!
 
As for your second issue. there's no reason to ask the question again because shortening it again won't do anything, there for the only option is to publish, so I just do that for them.

Then why is there a do/while loop there? If it serves no purpose, it shouldn't be there.

Without doing a lot of experimentation, I suspect the problems you're having with the lack of displayed text is due to the mixing of NSLog() calls with scanf() calls. You should use printf() or fprintf() rather than NSLog() and see if it changes anything.
 
The point of the do/while loop is so that if they mistype, or don't type "shorten" or "publish" it asks again until one of the terms is correctly entered. I'll try using some printf, but for that I would use the c strings, not the NSStrings, correct?
 
The point of the do/while loop is so that if they mistype, or don't type "shorten" or "publish" it asks again until one of the terms is correctly entered.

Read what you wrote there: "it asks again". Then go back and read what I wrote: there is no code in the loop that re-asks the "publish or shorten" question
 
I did some experimenting, and it appears that NSLog() is buffered or otherwise delayed from scanf(). As a result, the output and input are out of sync, and prompts aren't displayed before input is read.

Here are some fixes.

In tweet.h, add:

Code:
-(void) show:(NSString*) text;

In tweet.m add:

Code:
-(void) show:(NSString*) text
{
	printf( "%s\n", [text UTF8String] );
	fflush( stdout );
}

Everywhere you currently have NSLog, change it to use show:. I'll show two examples.

In main.m:

Code:
[myTweet show:@"Tweet is longer than 140 characters"];

In tweet.m, askToShorten:

Code:
[self show:[NSString stringWithFormat:
  @"Your tweet is %i characters long, %i more than 140. At this point, you can choose to shorten your tweet with our built in shortener or publish as is.", [usrTweet length], overflow ]];

There are only a couple of remaining NSLog calls, which I'll leave to you to convert.
 
Ok, I replaced all NSLog's with printf and....unless I did it wrong, still no luck :( Here's my code in case I wrote it wrong:

@interface
Code:
#import <Foundation/Foundation.h>


@interface tweet : NSObject {

	BOOL shorten;
	
	NSMutableArray *longWords;
	NSMutableArray *shortWords;
	
	NSString *reply;
	NSString *usrTweet;
	NSMutableString *shortTweet;
	
	const char *finalTweet;
	
	int overflow;
}


-(void) getTweet;
-(int) checkLength;
-(void) initObjects;
-(void) shorten;
-(BOOL) askToShorten;
-(void) print;
@end
@implementation
Code:
#import "tweet.h"
#import <Foundation/NSString.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSAutoreleasePool.h>
#import "stdio.h"

@implementation tweet

-(void) getTweet {
	char firstTweet[250];
	fgets(firstTweet, 250, stdin);
	if(firstTweet[strlen(firstTweet) - 1] == '\n') {
	   firstTweet[strlen(firstTweet) - 1] = 0;
	}
	usrTweet = [NSMutableString stringWithUTF8String: firstTweet];
	[self initObjects];
}

-(int) checkLength {
	if([shortTweet length] > 140) {
		overflow = [shortTweet length] - 140;
	} else shorten = NO;
	return [shortTweet length];
}

-(BOOL) askToShorten {
		printf("Your tweet is %i characters long, %i more than 140. At this point, you can choose to shorten your tweet with our built in shortener or publish as is.", [usrTweet length], overflow);
		char responseC[20];
		fgets(responseC, 20, stdin);
		if(responseC[strlen(responseC) - 1] == '\n') {
			responseC[strlen(responseC) - 1] = 0;
		}
	reply = [NSString stringWithUTF8String: responseC];
	
	int valid = 0;
	do{
		if ([reply isEqualToString:@"publish"]) {
			shorten = NO;
			valid = 1;
		} else if ([reply isEqualToString:@"shorten"]) {
			shorten = YES;
			valid = 1;
		} else { 
			printf("Please enter either 'shorten' or 'publish' to continue");
		}
	} while (valid == 0);
	return shorten;
}
	
-(void) initObjects {
	longWords = [NSMutableArray arrayWithObjects: @"because", @"for" , @"two", @"to", @"some", @"with", @"without", nil];
	shortWords = [NSMutableArray arrayWithObjects: @"b/c", @"4", @"2", @"2", @"sum", @"w/", @"w/o", nil];
	shortTweet = [NSMutableString stringWithString: usrTweet];
}


-(void) shorten {
	int amount = [longWords count];
	int i;
	for(i = 0; i < amount; ++i) {
		NSString *search = [NSString stringWithString: [longWords objectAtIndex: i]];
		NSString *replace = [NSString stringWithString: [shortWords objectAtIndex: i]];
		[shortTweet replaceOccurrencesOfString: search 
									withString: replace 
									   options: 0 
										 range: NSMakeRange (0, [shortTweet length])];
	}
	finalTweet = [@"%@", shortTweet UTF8String];
}
	
-(void) print {
	printf("Your tweet is: %c", finalTweet);
}

@end
main
Code:
#import <Foundation/Foundation.h>
#import "tweet.h"
#import <Foundation/NSString.h>
#import <Foundation/NSArray.h>

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
	tweet *myTweet = [[tweet alloc]init];
    [myTweet getTweet];
	if([myTweet checkLength] > 140) {
		printf("Tweet is longer than 140 characters");
		if([myTweet askToShorten] == YES) {
			[myTweet shorten];
			[myTweet print];
		} else [myTweet print];
	} else {
		[myTweet print];
	}
	[myTweet release];
	[pool drain];
    return 0;
}

For my sake, I hope I did something wrong :eek:
 
printf() is typically line-buffered. It only auto-flushes on a newline, if that, and there aren't any newlines in your text. If it isn't autoflushing, an fflush() is required.

Use the -show method approach I outlined above. It works for me.

EDIT:
I take it back: It works on earlier Xcode versions, not on Xcode 3. The output prompt gets printed, but any text I paste in for the tweet isn't read by the program. Unfortunately, I've forgotten how to fix this, but I think it's some setting in the Executable. More unfortunately, I have other things I have to do tonight, so I can't put any more time into this right now.
 
Read what you wrote there: "it asks again". Then go back and read what I wrote: there is no code in the loop that re-asks the "publish or shorten" question

OH :D:D Got it! I added an fgets() at the end, but before the loop terminates, so it should work properly now.

Secondly: WOW. SOOOOO sorry, I didn't see your post about the "show" method--I missed it. Ok, replaced the NSLog's with the show method that you wrote for me (thanks by the way) and, if it works on your computer, then I have a problem. The same thing happens :(

EDIT: Also, you keep referring to scanf(), but I'm not using scanf() any more, I'm using fgets(). Just pointing that out, may be a typo, maybe one is the same as the other IDK, but I just wanted to point that out.
 
Random bit of defensive coding advice: if you write comparisons like (0 == valid) instead of (valid == 0) then if you accidentally type = instead of == the compiler will catch it.
 
It doesn't matter much in your case because you do it in the main () function, but you have a memory leak: You don't release your autorelease pool.
 
In the last version of the code you posted, you still have
Code:
while (valid = 0)
which means you'll never re-ask the question.

Also, in your print function, you're printing a %c (a single character) yet are passing a char*. You introduced this bug when breaking up your code.

I'd have some suggestions for your design as well, if you like.
 
In the last version of the code you posted, you still have
Code:
while (valid = 0)
which means you'll never re-ask the question.
That's a typo it is fixed.
Also, in your print function, you're printing a %c (a single character) yet are passing a char*. You introduced this bug when breaking up your code.
Replaced %c with %s that should fix it (right?).
I'd have some suggestions for your design as well, if you like.
Suggest away!
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.