Finding out whether a string is numeric or not

IphoneObjective CCocoa TouchIosNsstring

Iphone Problem Overview


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)];

Iphone Solutions


Solution 1 - Iphone

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 - Iphone

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 - Iphone

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]+)?$"
  1. If exactly 4 digit numbers, without ".".
  • With pattern "^[0-9]{4}$".
  1. If digit numbers without ".", and the length is between 2 ~ 5.
  • With pattern "^[0-9]{2,5}$".
  1. 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 - Iphone

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 - Iphone

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.

Solution 6 - Iphone

Swift 3 solution if need to verify that the string has only digits:

CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: myString))

Solution 7 - Iphone

This original question was about Objective-C, but it was also posted years before Swift was announced. So, if you're coming here from Google and are looking for a solution that uses Swift, here you go:

let testString = "12345"
let badCharacters = NSCharacterSet.decimalDigitCharacterSet().invertedSet

if testString.rangeOfCharacterFromSet(badCharacters) == nil {
    print("Test string was a number")
} else {
    print("Test string contained non-digit characters.")
}

Solution 8 - Iphone

Swift 3 solution could be like:

extension String {
    
    var doubleValue:Double? {
        return NumberFormatter().number(from:self)?.doubleValue
    }
    
    var integerValue:Int? {
        return NumberFormatter().number(from:self)?.intValue
    }
    
    var isNumber:Bool {
        get {
            let badCharacters = NSCharacterSet.decimalDigits.inverted
            return (self.rangeOfCharacter(from: badCharacters) == nil)
        }
    }
}

Solution 9 - Iphone

to be clear, this functions for integers in strings.

heres a little helper category based off of John's answer above:

in .h file

@interface NSString (NumberChecking)

+(bool)isNumber:(NSString *)string;

@end

in .m file

#import "NSString+NumberChecking.h"

@implementation NSString (NumberChecking)

+(bool)isNumber {
    if([self rangeOfCharacterFromSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]].location == NSNotFound) {
        return YES;
    }else {
        return NO;
    }
}

@end

usage:

#import "NSString+NumberChecking.h"

if([someString isNumber]) {
    NSLog(@"is a number");
}else {
    NSLog(@"not a number");
}

Solution 10 - Iphone

An extension of @John Calsbeek's answer, and clarification of @Jeff and @gyratory circus's comments.

+ (BOOL)doesContainDigitsOnly:(NSString *)string
{
    NSCharacterSet *nonDigits = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
    
    BOOL containsDigitsOnly = [string rangeOfCharacterFromSet:nonDigits].location == NSNotFound;
    
    return containsDigitsOnly;
}

+ (BOOL)doesContainNonDigitsOnly:(NSString *)string
{
    NSCharacterSet *digits = [NSCharacterSet decimalDigitCharacterSet];
    
    BOOL containsNonDigitsOnly = [string rangeOfCharacterFromSet:digits].location == NSNotFound;
    
    return containsNonDigitsOnly;
}

The following can be added as category methods for NSString

- (BOOL)doesContainDigitsOnly
{
    NSCharacterSet *nonDigits = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
    
    BOOL containsDigitsOnly = [self rangeOfCharacterFromSet:nonDigits].location == NSNotFound;
    
    return containsDigitsOnly;
}

- (BOOL)doesContainNonDigitsOnly
{
    NSCharacterSet *digits = [NSCharacterSet decimalDigitCharacterSet];
    
    BOOL containsNonDigitsOnly = [self rangeOfCharacterFromSet:digits].location == NSNotFound;
    
    return containsNonDigitsOnly;
}

Solution 11 - Iphone

John Calsbeek's answer is nearly correct but omits some Unicode edge cases.

Per the documentation for decimalDigitCharacterSet, that set includes all characters categorized by Unicode as Nd. Thus their answer will accept, among others:

  • (U+0967 DEVANAGARI DIGIT ONE)
  • (U+1811 MONGOLIAN DIGIT ONE)
  • 𝟙 (U+1D7D9 MATHEMATICAL DOUBLE-STRUCK DIGIT ONE)

While in some sense this is correct — each character in Nd does map to a decimal digit — it's almost certainly not what the asker expected. As of this writing there are 610 code points categorized as Nd, only ten of which are the expected characters 0 (U+0030) through 9 (U+0039).

To fix the issue, simply specify exactly those characters that are acceptable:

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

Solution 12 - Iphone

Yet another option:

- (BOOL)isValidNumber:(NSString*)text regex:(NSString*)regex {
    @try {
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];
        return [predicate evaluateWithObject:text];
    }
    @catch (NSException *exception) {
        assert(false); 
        return NO;
    }
}

Usage example:

BOOL isValid = [self isValidNumber:@"1234" regex:@"^[0-9]+$"];

Solution 13 - Iphone

Test if a string is a number Might be Helpful

int i = [@"12.3" rangeOfCharacterFromSet: [ [NSCharacterSet characterSetWithCharactersInString:@"0123456789."] invertedSet] ].location;

if (i == NSNotFound) {
     //is a number
}

Solution 14 - Iphone

Swift extension :

extension NSString {
func isNumString() -> Bool {
    let numbers = NSCharacterSet(charactersInString: "0123456789.").invertedSet
    let range = self.rangeOfCharacterFromSet(numbers).location
    if range == NSNotFound {
        return true
    }
    return false
}  }

Solution 15 - Iphone

For Swift 3

var onlyDigits: CharacterSet = CharacterSet.decimalDigits.inverted
if testString.rangeOfCharacter(from: onlyDigits) == nil {
  // String only consist digits 0-9
}

Solution 16 - Iphone

When you have digits that are from mixed languages, that use (or don't) the 0-9 digits formats, you will need to run a regex that will look for any number, next thing is to convert all digits to be 0-9 format (if you need the actual value):

// Will look for any language digits
let regex = try NSRegularExpression(pattern: "[^[:digit:]]", options: .caseInsensitive)
let digitsString = regex.stringByReplacingMatches(in: string,
                                                         options: NSRegularExpression.MatchingOptions(rawValue: 0),
                                                         range: NSMakeRange(0, string.count), withTemplate: "")
// Converting the digits to be 0-9 format
let numberFormatter = NumberFormatter()
numberFormatter.locale = Locale(identifier: "EN")
let finalValue = numberFormatter.number(from: digitsString)

if let finalValue = finalValue {
  let actualValue = finalValue.doubleValue
}

Solution 17 - Iphone

The easiest and most reliable way is trying to cast as Double, if the result is nil - it can't be formed into a legit number.

let strings = ["test", "123", "123.2", "-123", "123-3", "123..22", ".02"]
let validNumbers = strings.compactMap(Double.init)

print(validNumbers)
// prints [123.0, 123.2, -123.0, 0.02]

More info in the documentation: https://developer.apple.com/documentation/swift/double/2926277-init

Solution 18 - Iphone

Simple wrote the string extension:

extension String {
    var isNumeric: Bool {
        return self.rangeOfCharacter(from: CharacterSet.decimalDigits.inverted) == nil
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAbhinavView Question on Stackoverflow
Solution 1 - IphoneJohn CalsbeekView Answer on Stackoverflow
Solution 2 - IphoneSamView Answer on Stackoverflow
Solution 3 - IphoneAechoLiuView Answer on Stackoverflow
Solution 4 - IphoneRegexidentView Answer on Stackoverflow
Solution 5 - IphoneTommyView Answer on Stackoverflow
Solution 6 - Iphoneplamkata__View Answer on Stackoverflow
Solution 7 - IphoneTwoStrawsView Answer on Stackoverflow
Solution 8 - IphonehhammView Answer on Stackoverflow
Solution 9 - IphoneMrTristanView Answer on Stackoverflow
Solution 10 - IphoneAbdurrahman Mubeen AliView Answer on Stackoverflow
Solution 11 - IphoneravronView Answer on Stackoverflow
Solution 12 - IphoneLuisCienView Answer on Stackoverflow
Solution 13 - IphoneAshok KumarView Answer on Stackoverflow
Solution 14 - IphoneBhavesh PatelView Answer on Stackoverflow
Solution 15 - IphoneMert CelikView Answer on Stackoverflow
Solution 16 - IphoneOhadMView Answer on Stackoverflow
Solution 17 - IphoneArielView Answer on Stackoverflow
Solution 18 - IphoneUmair AliView Answer on Stackoverflow