Finding out whether a string is numeric or not

64,150

Solution 1

Here's one way that doesn't rely on the limited precision of attempting to parse the string as a number:

NSCharacterSet* notDigits = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
if ([newString rangeOfCharacterFromSet:notDigits].location == NSNotFound)
{
    // newString consists only of the digits 0 through 9
}

See +[NSCharacterSet decimalDigitCharacterSet] and -[NSString rangeOfCharacterFromSet:].

Solution 2

I'd suggest using the numberFromString: method from the NSNumberFormatter class, as if the number is not valid, it will return nil; otherwise, it will return you an NSNumber.

NSNumberFormatter *nf = [[[NSNumberFormatter alloc] init] autorelease];
BOOL isDecimal = [nf numberFromString:newString] != nil;

Solution 3

Validate by regular expression, by pattern "^[0-9]+$", with following method -validateString:withPattern:.

[self validateString:"12345" withPattern:"^[0-9]+$"];
  1. If "123.123" is considered
    • With pattern "^[0-9]+(.{1}[0-9]+)?$"
  2. If exactly 4 digit numbers, without ".".
    • With pattern "^[0-9]{4}$".
  3. If digit numbers without ".", and the length is between 2 ~ 5.
    • With pattern "^[0-9]{2,5}$".
  4. With minus sign: "^-?\d+$"

The regular expression can be checked in the online web site.

The helper function is as following.

// Validate the input string with the given pattern and
// return the result as a boolean
- (BOOL)validateString:(NSString *)string withPattern:(NSString *)pattern
{
    NSError *error = nil;
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:&error];

    NSAssert(regex, @"Unable to create regular expression");

    NSRange textRange = NSMakeRange(0, string.length);
    NSRange matchRange = [regex rangeOfFirstMatchInString:string options:NSMatchingReportProgress range:textRange];

    BOOL didValidate = NO;

    // Did we find a matching range
    if (matchRange.location != NSNotFound)
        didValidate = YES;

    return didValidate;
}

Swift 3 version:

Test in playground.

import UIKit
import Foundation

func validate(_ str: String, pattern: String) -> Bool {
    if let range = str.range(of: pattern, options: .regularExpression) {
        let result = str.substring(with: range)
        print(result)
        return true
    }
    return false
}

let a = validate("123", pattern: "^-?[0-9]+")
print(a)

Solution 4

You could create an NSScanner and simply scan the string:

NSDecimal decimalValue;
NSScanner *sc = [NSScanner scannerWithString:newString];
[sc scanDecimal:&decimalValue];
BOOL isDecimal = [sc isAtEnd];

Check out NSScanner's documentation for more methods to choose from.

Solution 5

I think the easiest way to check that every character within a given string is numeric is probably:

NSString *trimmedString = [newString stringByTrimmingCharactersInSet:[NSCharacterSet decimalDigitCharacterSet]];

if([trimmedString length])
{
    NSLog(@"some characters outside of the decimal character set found");
}
else
{
    NSLog(@"all characters were in the decimal character set");
}

Use one of the other NSCharacterSet factory methods if you want complete control over acceptable characters.

Share:
64,150
Abhinav
Author by

Abhinav

Updated on July 08, 2022

Comments

  • Abhinav
    Abhinav almost 2 years

    How can we check if a string is made up of numbers only. I am taking out a substring from a string and want to check if it is a numeric substring or not.

    NSString *newString = [myString substringWithRange:NSMakeRange(2,3)];
    
  • Tommy
    Tommy almost 13 years
    Wouldn't that just tell you whether at least the first character is numeric?
  • Regexident
    Regexident almost 13 years
    My bad. Forgot the final call to isAtEnd.
  • Tommy
    Tommy almost 13 years
    Are you sure it won't return a valid number e.g. for @"124sbd"?
  • Regexident
    Regexident almost 13 years
    No, both NSNumberFormatter and NSScanner would return NO for your string. Also worth knowing: they also return YES for numbers padded with whitespace. Btw, I just added some actual code snippet to your answer, hope you don't mind.
  • Tommy
    Tommy almost 13 years
    NSScanner should return 'YES' for that string, per the documentation, having found "a valid integer representation". Furthermore, it did exactly that in a test on iOS 4.3.2. However, NSNumberFormatter did return nil.
  • Damian
    Damian about 12 years
    This doesn't catch '.' which is a requirement for me. Answer from @John Calsbeek worked nicely.
  • nembleton
    nembleton almost 12 years
    I like this way. Although it doesn't seem very clean, it's a very easy and core foundation-friendly way of checking if there are "non-digits" in a NSString. + I think that it's much faster than any other robot-based ways ( although we're probably not looking much at performance here ).
  • string.Empty
    string.Empty over 10 years
    this is true for @"231.123" so: // newString consists only of the digits 0 through 9 and the . character
  • codrut
    codrut over 10 years
    NSMutableCharacterSet *digitsAndDots = [NSMutableCharacterSet decimalDigitCharacterSet]; [digitsAndDots addCharactersInString:@"."]; NSCharacterSet *notDigitsNorDots = [digitsAndDots invertedSet]; //also, thanx for bringing in "invertedSet". I didn't know about its existence
  • Ash
    Ash over 9 years
    Since you've got a mutable set there anyway, you could just call the 'invert' method on it rather than making a whole new set.
  • Brody Robertson
    Brody Robertson almost 9 years
    I believe that stringByTrimmingCharactersInSet only trims the begin and end of the String
  • Tommy
    Tommy almost 9 years
    @BrodyRobertson: it does. Which matters not in the slightest for this answer. What example do you think this test would fail for?
  • Brody Robertson
    Brody Robertson almost 9 years
    @Tommy, After reevaluating I understand your answer and it is valid and will upvote but you need you to edit to allow me to revote
  • markckim
    markckim over 8 years
    note: this method considers @"" to be a number; if applicable, you may need to also check for [newString lenght] > 0. please let me know if i'm wrong.
  • Supertecnoboff
    Supertecnoboff over 7 years
    @NicolasTyler Are you sure? This is only working for whole numbers in my case. For example 85, 342 and 2134 all work fine, but 234.525 does not.
  • string.Empty
    string.Empty over 7 years
    @Supertecnoboff It seems this is something that has changed in IOS. This can be used instead to be sure: [[NSCharacterSet characterSetWithCharactersInString:@"0123456789."] invertedSet]]
  • Supertecnoboff
    Supertecnoboff over 7 years
    @NicolasTyler interesting but does that cover all locals though? Like don't some use a comma instead of a dot?
  • string.Empty
    string.Empty over 7 years
    @Supertecnoboff At the time when I did this 3 years ago the character set included . That doesnt seem to be the case anymore. I never tested if commas were included on other locals. But using characterSetWithCharactersInString could be used with any characters, if you are worried.
  • Iulian Onofrei
    Iulian Onofrei over 7 years
    Why is this page saying it's available from iOS 10+ if this answer in from 2011?
  • Jeff
    Jeff over 7 years
    Why not just NSCharacterSet *areDigits = [NSCharacterSet decimalDigitCharacterSet] and then test for != NSNotFound?
  • Biniou
    Biniou over 7 years
    What happens for a string with hundreds of characters all digits? Is there a limit to the NSNumber created?
  • gyratory circus
    gyratory circus about 7 years
    @Jeff You'd encounter false positives for strings that contain numerical characters, but do not represent numerical values, such asg2g
  • Mathi Arasan
    Mathi Arasan almost 7 years
    I tried like this for Swift 3. func numberOnly(string: String) -> Int { let expression = "" let regex = NSRegularExpression.init(pattern: expression, options: .caseInsensitive) let numberOfMatches = regex.numberOfMatches(in: string, options: .reportProgress, range: NSRange.init(location: 0, length: string.characters.count)) if numberOfMatches == 0 { return Int(string)! } return 0 } And I got error Playground execution aborted: error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
  • Mathi Arasan
    Mathi Arasan almost 7 years
    I don't know that correct one for swift 3. Correct me if I wrong.
  • Dannie P
    Dannie P over 6 years
    Clean and nice, best solution. I like that this does not do unnecessary .inverted and other actions.
  • KM Gorbunov
    KM Gorbunov over 5 years
    invertedSet What is the cost of that operation? It should be a huge NSCharacterSet.
  • John Calsbeek
    John Calsbeek over 5 years
    @KMGorbunov I don't think that NSCharacterSet is actually storing a backing set that contains every single character in the set. I suspect invertedSet is returning a class that wraps the set it was created form, and returns the opposite value for all queries. But I don't know for certain.
  • ravron
    ravron over 5 years
    This solution will accept a variety of likely-unexpected strings: 𝟷𝟸𝟹, 𝟙𝟚𝟛, १२३, ١٢٣, and ১২৩, to name a few. My answer, among others, addresses this issue.
  • LimeRed
    LimeRed about 5 years
    How about just return matchRange.location != NSNotFound;
  • Thomas
    Thomas over 4 years
    what about the minus sign?
  • Thomas
    Thomas over 4 years
    the best answer is the scanner (see Regexident's answer), it can work for numeric strings too (the formatter didn't work for numbers like -1.2 for me)
  • AechoLiu
    AechoLiu over 4 years
    @Thomas With ^-?\d+$, I verified it on the site: regex101.com
  • Alexandre Odet
    Alexandre Odet about 2 years
    Super clean solution, really like it !