1

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...)

Richard J. Ross III
  • 55,009
  • 24
  • 135
  • 201
  • You really should not be using an exception for this. Exceptions in obj-c are intended to signal programmer errors. Data errors, such as this one, should use an NSError*-based API. – Lily Ballard Nov 04 '10 at 09:18
  • Sorry, my boss and I have a problem with `nils` just running arround in code... – Richard J. Ross III Nov 04 '10 at 11:04
  • @Kevin Ballard: With this API there is no way to indicate an error value since any int is valid. – JeremyP Nov 04 '10 at 11:28
  • @JeremyP: A couple of solutions come to mind. The first is to just return the NSNumber* and let the caller call `-intValue` on it. The second is to guarantee that a valid call always nils out the error parameter and declare that a return value of 0 means the caller needs to check the parameter. A third way is to return a BOOL and have the int be returned in an out-parameter. – Lily Ballard Nov 05 '10 at 03:42

2 Answers2

4

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:.

JeremyP
  • 84,577
  • 15
  • 123
  • 161
1

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];
joshpaul
  • 953
  • 8
  • 12