NSNumberFormatter iOS 3.2 Bug? - iphone

I have code that I am porting from iOS 4 to iOS 3.2 for a demo project on an iPad. I have this code:
+(int) parseInt:(NSString *)str
{
NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
[nf setAllowsFloats:NO];
[nf setMaximum:[NSNumber numberWithInt:INT_MAX]];
[nf setMinimum:[NSNumber numberWithInt:INT_MIN]];
#try {
NSNumber *num = [nf numberFromString:str];
if (!num)
#throw [DataParseException exceptionWithDescription:#"the data is not in the correct format."];
return [num intValue];
}
#finally {
[nf release];
}
}
This works spendid on iOS 4, throwing exceptions when a string (such as a date, which I am having problems with):
1/1/2010
For some reason, num isn't nil, it has the value 1, while on iOS 4, It is nil as expected. I was originally using NSScanner because it was easier than NSNumberFormatter to use, but I ran into the same problem, it doesn't parse the entire string, just the first number in the string.
Is there something I can do to fix this, or must I manually create an int parser. I would prefer not to use a C-Based approach, but if I must, I will.
EDIT: I have updated my code to this:
+(int) parseInt:(NSString *)str
{
NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
[nf setAllowsFloats:NO];
[nf setMaximum:[NSNumber numberWithInt:INT_MAX]];
[nf setMinimum:[NSNumber numberWithInt:INT_MIN]];
#try {
IF_IOS4_OR_GREATER
(
NSNumber *num = [nf numberFromString:str];
if (!num)
#throw [DataParseException exceptionWithDescription:#"the data is not in the correct format."];
return [num intValue];
)
else {
NSNumber *num = nil;
NSRange range = NSMakeRange(0, str.length);
NSError *err = nil;
[nf getObjectValue:&num forString:str range:&range error:&err];
if (err)
#throw [DataParseException exceptionWithDescription:[err description]];
if (range.length != [str length])
#throw [DataParseException exceptionWithDescription:#"Not all of the number is a string!"];
if (!num)
#throw [DataParseException exceptionWithDescription:#"the data is not in the correct format."];
return [num intValue];
}
}
#finally {
[nf release];
}
}
And I get a EXC_BAD_ACCESS signal when I try to parse the string 1/1/2001. Any Ideas?
(iOS 4 or greater is defined here: http://cocoawithlove.com/2010/07/tips-tricks-for-conditional-ios3-ios32.html)
I have a new error: when I parse the number, it isn't exact (exact as in it has multiple decimal points when using same code for floats) anymore.... how can I fix that?? (I might just use #joshpaul's answer...)

I couldn't find anything specific to iOS but the data formatting guide has this interesting paragraph:
Note: Prior to Mac OS v10.6, the implementation of getObjectValue:forString:errorDescription: would return YES and an object value even if only part of the string could be parsed. This is problematic because you cannot be sure what portion of the string was parsed. For applications linked on or after Mac OS v10.6, this method instead returns an error if part of the string cannot be parsed. You can use getObjectValue:forString:range:error: to get the old behavior; this method returns the range of the substring that was successfully parsed.
I would not be at all surprised if numberFromString: was implemented in terms of the above method and the iOS 3.2 NSNumberFormatter is based on 10.5 whereas the iOS 4 version is 10.6.
Just my guess.
If you pass 1/1/2010 on iOS 3.2 the 1 will be parsed and the rest ignored. You could test the hypothesis by seeing if you get 2 when you pass 2/1/2010.
The work around would seem to be to use getObjectValue:forString:range:error:.

So the basic [str intValue] doesn't work? Nor, [scanner scanInt:&int]?
What about using NSCharacterSet, i.e.:
NSString *test = [str stringByTrimmingCharactersInSet:[NSCharacterSet decimalDigitCharacterSet]];
if ([test length]) #throw ...;
return [str intValue];

Related

Integer value with comma separated not getting saved to core data in iOS

In my iPhone app am saving data to core data. I have to save one comma separated integer value.
NSNumberFormatter * formatter = [NSNumberFormatter new];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
[formatter setMaximumFractionDigits:0];
NSString * newString = [formatter stringFromNumber:[NSNumber numberWithInt: [textField.text intValue]]];
textField.text=newString;
In textfield its displaying properly. But when I'm saving [textField.text intValue], getting only first part before comma.
For example: If the text is 15,000 while saving am getting only 15. Please help!!!
you want [formatter numberFromString:textField.text], not [textField.text intValue]
The value can be extracted by replacing the comma.
Use
yourCommaString = [yourCommaString stringByReplacingOccurrencesOfString:#"," withString:#""];
Now convert it to required integer value.
[NSNumber numberWithInteger:yourCommaString.integerValue]

What is a better way to check if a string is an integer on iPhone?

The following code will identify if a string is an integer - that is, the string contains only digits. But, I hate this code. What is a better way?
NSString *mightBeAnInteger = fooString;
int intValue = [fooString intValue];
if (intValue > 0
&& [[NSString stringWithFormat:#"%d",intValue] isEqualToString:mightBeAnInteger]) {
NSLog(#"mightBeAnInteger is an integer");
}
[fooString rangeOfCharacterFromSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]].location == NSNotFound will be YES if the string only has number characters in it.
Note that this doesn't catch negative numbers, so you'll have to add in the negative sign (probably by grabbing a mutableCopy and adding the sign).
-(BOOL) stringIsNumeric:(NSString *) str {
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
NSNumber *number = [formatter numberFromString:str];
[formatter release];
return !!number; // If the string is not numeric, number will be nil
}
source: Objective c: Check if integer/int/number

What is wrong with this string format (different behavior on simulator and device)?

I have this block of code executed when pressing a number:
NSString *currentValue = [#"" stringByAppendingFormat:#"%.02f", [[[[[textField text] stringByReplacingOccurrencesOfString:#"." withString:#""] stringByReplacingOccurrencesOfString:#"," withString:#""] stringByReplacingOccurrencesOfString:#" " withString:#""] doubleValue]/100.0f];
//I am using this to obtain always a number with 2 decimals.
NSNumberFormatter *f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterDecimalStyle];
[f setMinimumFractionDigits:2];
[f setMaximumFractionDigits:2];
[f setGroupingSeparator:#" "];
NSNumber *currentNumberValue = [f numberFromString:currentValue];
NSLog(#"1: %#", currentValue);
NSLog(#"2: %#", [currentNumberValue stringValue]);
Now if I run this in the simulator and press 3 I get the following results:
1: 0.03
2: 0.03
If I run it on the device I have:
1: 0.03
2: 0
So basically on the device the formated number is 0.
What I have also noticed is that on the simulator I get '.' as a decimal separator and on the device I have ','.
And because of this it never gets further. Any number I press it still remains 0.
What seems to be the problem?
Your device is apparently set to a European (or wherever) locale that uses , as the decimal separator. Try adding this line after the line where you alloc and init your number formatter:
[f setDecimalSeparator:#"."];
Or use the setLocale method (or change the locale your device is set to).
Try it like this:
NSString *currentValue = [textField text];
float currentFloat = [currentValue floatValue];
NSLog(#"%.2f",currentFloat); //string representation of floatValue
NSLog(#"%#",currentValue); //string currentValue

markup text parser like stackoverflow's formatter in Objective-C

I'm in the process of creating a markup editor in Objective C. I require the following functionality:
Recognise the demarcation of a block eg **block**
Delete the start and end "tags" eg "The next text is **bold**" becomes "The next text is bold"
Determine the start and end positions of the marked-up text in the new context: "The next text is bold"
Edit:
As I may expand the syntax in the future (it will be very limited at the moment), it is important that parsing be top-down such that the start and end positions of the text always correspond with the resulting text. For this reason regex may not be the best solution.
What is the best way to do this?
In the end went for regex approach using RegexKitLite
The code below is not fully tested but does work with the case St3fan pointed out.
- (NSArray *) scanContent:(NSMutableString **)content {
NSMutableArray *tokens = [[NSMutableArray alloc] init];
NSArray *captureRegex = [[NSArray alloc] initWithObjects:
#"\\[\\[(.*?)\\]\\]",#"\\*\\*(.*?)\\*\\*", nil];
NSArray *tokenID = [[NSArray alloc] initWithObjects:
#"Italic",#"Bold", nil];
int index = 0;
for (NSString*capture in captureRegex) {
NSRange captureRange;
NSRange stringRange;
stringRange.location = 0;
stringRange.length = [*content length];
do {
captureRange = [*content rangeOfRegex:capture inRange:stringRange];
if ( captureRange.location != NSNotFound ) {
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
[dictionary setObject:[tokenID objectAtIndex:index] forKey:#"Token"];
[dictionary setObject:[NSNumber numberWithInt:captureRange.location]
forKey:#"Start"];
[dictionary setObject:[NSNumber numberWithInt:captureRange.length]
forKey:#"Length"];
[tokens addObject:dictionary];
for (NSMutableDictionary *dict in tokens) {
NSNumber *nRange = [dict objectForKey:#"Start"];
int start = [nRange intValue];
if (start > captureRange.location) {
nRange = [NSNumber numberWithInt:start - 4]; // Removing 4 characters
[dict setObject:nRange forKey:#"Start"];
}
if (start == captureRange.location) {
NSString *data = [*content stringByMatching:capture options:RKLMultiline inRange:captureRange capture:1 error:NULL];
NSLog(#"data: %#",data);
[*content replaceOccurrencesOfRegex:capture withString:data range:captureRange];
NSLog(#"Replaced Content: %#",*content);
}
}
stringRange.location = captureRange.location + captureRange.length -4;
stringRange.length = [*content length] - stringRange.location;
}
}
while ( captureRange.location != NSNotFound );
index++;
}
return tokens;
}
MarkDown Sharp, the markdown processor used on the StackExchange websites, is open source. Take a look at the file, perhaps you can see how they do it or port it to objective-c.
Perhaps better yet, take a look at this question: "What is the simplest implementation of Markdown for a Cocoa application?"
It links to an open source application called MarkdownLive which uses a C implementation of Markdown called discount, and also provides an objective-c wrapper for it.

NSString stringWithFormat question

I am trying to build a small table using NSString. I cannot seem to format the strings properly.
Here is what I have
[NSString stringWithFormat:#"%8#: %.6f",e,v]
where e is an NSString from somewhere else, and v is a float.
What I want is output something like this:
Grapes: 20.3
Pomegranates: 2.5
Oranges: 15.1
What I get is
Grapes:20.3
Pomegranates:2.5
Oranges:15.1
How can I fix my format to do something like this?
you could try using - stringByPaddingToLength:withString:startingAtIndex:
NSDictionary* fruits = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat:20.3], #"Grapes",
[NSNumber numberWithFloat:2.5], #"Pomegranates",
[NSNumber numberWithFloat:15.1], #"Oranges",
nil];
NSUInteger longestNameLength = 0;
for (NSString* key in [fruits allKeys])
{
NSUInteger keyLength = [key length];
if (keyLength > longestNameLength)
{
longestNameLength = keyLength;
}
}
for (NSString* key in [fruits allKeys])
{
NSUInteger keyLength = [key length];
NSNumber* object = [fruits objectForKey:key];
NSUInteger padding = longestNameLength - keyLength + 1;
NSLog(#"%#", [NSString stringWithFormat:#"%#:%*s%5.2f", key, padding, " ", [object floatValue]]);
}
Output:
Oranges: 15.10
Pomegranates: 2.50
Grapes: 20.30
The NSNumberFormatter class is the way to go!
Example:
NSNumberFormatter *numFormatter = [[NSNumberFormatter alloc] init];
[numFormatter setPaddingCharacter:#"0"];
[numFormatter setFormatWidth:2];
NSString *paddedString = [numFormatter stringFromNumber:[NSNumber numberWithInteger:integer]];
[numFormatter release];
I think you want something like
[NSString stringWithFormat:#"%-9# %6.1f",[e stringByAppendingString:#":"],v]
since you want padding in front of the float to make it fit the column, though if the NSString is longer than 8, it will break the columns.
%-8f left-aligns the string in a 9-character-wide column (9-wide since the : is appended to the string beforehand, which is done so the : is at the end of the string, not after padding spaces); %6.1f right-aligns the float in a 6-char field with 1 decimal place.
edit: also, if you're viewing the output as if it were HTML (through some sort of web view, for instance), that may be reducing any instances of more than one space to a single space.