32

I have a string that is being generate from a formula, however I only want to use the string as long as all of its characters are numeric, if not that I want to do something different for instance display an error message.

I have been having a look round but am finding it hard to find anything that works along the lines of what I am wanting to do. I have looked at NSScanner but I am not sure if its checking the whole string and then I am not actually sure how to check if these characters are numeric

- (void)isNumeric:(NSString *)code{

    NSScanner *ns = [NSScanner scannerWithString:code];
    if ( [ns scanFloat:NULL] ) //what can I use instead of NULL?
    {
        NSLog(@"INSIDE IF");
    }
    else {
    NSLog(@"OUTSIDE IF");
    }
}

So after a few more hours searching I have stumbled across an implementation that dose exactly what I am looking for.

so if you are looking to check if their are any alphanumeric characters in your NSString this works here

-(bool) isNumeric:(NSString*) hexText
{

    NSNumberFormatter* numberFormatter = [[[NSNumberFormatter alloc] init] autorelease];

    NSNumber* number = [numberFormatter numberFromString:hexText];

    if (number != nil) {
        NSLog(@"%@ is numeric", hexText);
        //do some stuff here      
        return true;
    }

    NSLog(@"%@ is not numeric", hexText);
    //or do some more stuff here
    return false;
}

hope this helps.

C.Johns
  • 10,185
  • 20
  • 102
  • 156
  • If anyone is interested I created a gist with some of the solutions presented here in an NSString category -- [NSString+isNumeric](https://gist.github.com/somethingkindawierd/4962594) – Jonathan Beebe Feb 15 '13 at 19:11

7 Answers7

55

Something like this would work:

@interface NSString (usefull_stuff)
- (BOOL) isAllDigits;
@end

@implementation NSString (usefull_stuff)

- (BOOL) isAllDigits
{
    NSCharacterSet* nonNumbers = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
    NSRange r = [self rangeOfCharacterFromSet: nonNumbers];
    return r.location == NSNotFound && self.length > 0;
}

@end

then just use it like this:

NSString* hasOtherStuff = @"234 other stuff";
NSString* digitsOnly = @"123345999996665003030303030";

BOOL b1 = [hasOtherStuff isAllDigits];
BOOL b2 = [digitsOnly isAllDigits];

You don't have to wrap the functionality in a private category extension like this, but it sure makes it easy to reuse..

I like this solution better than the others since it wont ever overflow some int/float that is being scanned via NSScanner - the number of digits can be pretty much any length.

TomSwift
  • 39,369
  • 12
  • 121
  • 149
15

Consider NSString integerValue - it returns an NSInteger. However, it will accept some strings that are not entirely numeric and does not provide a mechanism to determine strings which are not numeric at all. This may or may not be acceptable.

For instance, " 13 " -> 13, "42foo" -> 42 and "helloworld" -> 0.

Happy coding.


Now, since the above was sort of a tangent to the question, see determine if string is numeric. Code taken from link, with comments added:

BOOL isNumeric(NSString *s)
{
   NSScanner *sc = [NSScanner scannerWithString: s];
   // We can pass NULL because we don't actually need the value to test
   // for if the string is numeric. This is allowable.
   if ( [sc scanFloat:NULL] )
   {
      // Ensure nothing left in scanner so that "42foo" is not accepted.
      // ("42" would be consumed by scanFloat above leaving "foo".)
      return [sc isAtEnd];
   }
   // Couldn't even scan a float :(
   return NO;
}

The above works with just scanFloat -- e.g. no scanInt -- because the range of a float is much larger than that of an integer (even a 64-bit integer).

This function checks for "totally numeric" and will accept "42" and "0.13E2" but reject " 13 ", "42foo" and "helloworld".

Community
  • 1
  • 1
  • If I have a NSString in another method that I want to pass to this bool method how do I do that? I am used to just passing variables like so **[self methodname:valuetopass];** – C.Johns Jul 10 '11 at 22:50
  • Well, it's similar... `[objWhichHasMethod isNumber: &theNSStringToPass]` The `&` gets the "pointer" to `theNSStringToPass` (where I am assuming that `theNSStringToPass` is of type `NSString`). –  Jul 11 '11 at 19:35
  • @C.Johns However, in many cases, I'd expect the variable to be passed in to *already* be of type `NSString*` and not having it as such ... sounds suspect. –  Jul 11 '11 at 19:48
  • Warning: this doesn't seems to work with numbers with a decimal separator other than a dot. In some languages, the separator is a comma. – mrmuggles Dec 14 '13 at 17:13
  • @mrmuggles: Use +localizedScannerWithString: to create localized instance of the scanner (alternatively, call -setLocale: on scanner instance). Great answer, btw. – david a. Jun 24 '14 at 11:20
9

It's very simple.

+ (BOOL)isStringNumeric:(NSString *)text
{
    NSCharacterSet *alphaNums = [NSCharacterSet decimalDigitCharacterSet];
    NSCharacterSet *inStringSet = [NSCharacterSet characterSetWithCharactersInString:text];        
    return [alphaNums isSupersetOfSet:inStringSet];
}
arturdev
  • 10,884
  • 2
  • 39
  • 67
8

Like this:

- (void)isNumeric:(NSString *)code{

    NSScanner *ns = [NSScanner scannerWithString:code];
    float the_value;
    if ( [ns scanFloat:&the_value] )
    {
        NSLog(@"INSIDE IF");
        // do something with `the_value` if you like
    }
    else {
    NSLog(@"OUTSIDE IF");
    }
}
Matt Connolly
  • 9,757
  • 2
  • 65
  • 61
  • For just the check, with the scanned value is not required, does `NULL` suffice or must a valid address be passed in? (The reference indicates that `NULL` is OKAY here.) –  Jul 10 '11 at 22:06
  • 1
    @WrightsCS a NSString contains text. The text may *represent* an NSInteger, but the text itself is "just data". In this case, just use a different `scanXYZ` to read in the textual representation into the appropriate variable. Note that `scanInt` takes an `int*`, not an `NSInteger`. –  Jul 10 '11 at 22:06
  • @pst Yes, but this would seem redundant if you need to check for an NSInteger; you would need a new `isNumeric:` method. – WrightsCS Jul 10 '11 at 22:14
  • Unless you accept that integers will scan as floats. ie: scanFloat will work on integers as well as floats. – Matt Connolly Jul 10 '11 at 22:30
2

Faced same problem in Swift.
In Swift you should use this code, according TomSwift's answer:

func isAllDigits(str: String) -> Bool {

    let nonNumbers = NSCharacterSet.decimalDigitCharacterSet()

    if let range = str.rangeOfCharacterFromSet(nonNumbers) {
        return true
    }
    else {
        return false
    }
}

P.S. Also you can use other NSCharacterSets or their combinations to check your string!

0

For simple numbers like "12234" or "231231.23123" the answer can be simple.

There is a transformation law for int numbers: when string with integer transforms to int (or long) number and then, again, transforms it back to another string these strings will be equal.

In Objective C it will looks like:

NSString *numStr=@"1234",*num2Str=nil;
num2Str=[NSString stringWithFormat:@"%lld",numStr.longlongValue];

if([numStr isEqualToString: num2Str]) NSLog(@"numStr is an integer number!");

By using this transformation law we can create solution
to detect double or long numbers:

NSString *numStr=@"12134.343"
NSArray *numList=[numStr componentsSeparatedByString:@"."];

if([[NSString stringWithFormat:@"%lld", numStr.longLongValue] isEqualToString:numStr]) NSLog(@"numStr is an integer number"); 
else 
if( numList.count==2 &&
               [[NSString stringWithFormat:@"%lld",((NSString*)numList[0]).longLongValue] isEqualToString:(NSString*)numList[0]] &&
               [[NSString stringWithFormat:@"%lld",((NSString*)numList[1]).longLongValue] isEqualToString:(NSString*)numList[1]] )
            NSLog(@"numStr is a double number");
else 
NSLog(@"numStr is not a number");

I did not copy the code above from my work code so can be some mistakes, but I think the main point is clear. Of course this solution doesn't work with numbers like "1E100", as well it doesn't take in account size of integer and fractional part. By using the law described above you can do whatever number detection you need.

sinoptic
  • 303
  • 3
  • 12
0

C.Johns' answer is wrong. If you use a formatter, you risk apple changing their codebase at some point and having the formatter spit out a partial result. Tom's answer is wrong too. If you use the rangeOfCharacterFromSet method and check for NSNotFound, it'll register a true if the string contains even one number. Similarly, other answers in this thread suggest using the Integer value method. That is also wrong because it will register a true if even one integer is present in the string. The OP asked for an answer that ensures the entire string is numerical. Try this:

NSCharacterSet *searchSet = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];

Tom was right about this part. That step gives you the non-numerical string characters. But then we do this:

NSString *trimmedString = [string stringByTrimmingCharactersInSet:searchSet];

return (string.length == trimmedString.length);

Tom's inverted character set can TRIM a string. So we can use that trim method to test if any non numerals exist in the string by comparing their lengths.