Finding out whether a string is numeric or not
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]+$"];
- If "123.123" is considered
- With pattern
"^[0-9]+(.{1}[0-9]+)?$"
- With pattern
- If exactly 4 digit numbers, without
"."
.- With pattern
"^[0-9]{4}$"
.
- With pattern
- If digit numbers without
"."
, and the length is between 2 ~ 5.- With pattern
"^[0-9]{2,5}$"
.
- With pattern
- 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.
Abhinav
Updated on July 08, 2022Comments
-
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 almost 13 yearsWouldn't that just tell you whether at least the first character is numeric?
-
Regexident almost 13 yearsMy bad. Forgot the final call to
isAtEnd
. -
Tommy almost 13 yearsAre you sure it won't return a valid number e.g. for @"124sbd"?
-
Regexident almost 13 yearsNo, both NSNumberFormatter and NSScanner would return
NO
for your string. Also worth knowing: they also returnYES
for numbers padded with whitespace. Btw, I just added some actual code snippet to your answer, hope you don't mind. -
Tommy almost 13 yearsNSScanner 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 about 12 yearsThis doesn't catch '.' which is a requirement for me. Answer from @John Calsbeek worked nicely.
-
nembleton almost 12 yearsI 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 over 10 yearsthis is true for @"231.123" so: // newString consists only of the digits 0 through 9 and the
.
character -
codrut over 10 yearsNSMutableCharacterSet *digitsAndDots = [NSMutableCharacterSet decimalDigitCharacterSet]; [digitsAndDots addCharactersInString:@"."]; NSCharacterSet *notDigitsNorDots = [digitsAndDots invertedSet]; //also, thanx for bringing in "invertedSet". I didn't know about its existence
-
Ash over 9 yearsSince 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 almost 9 yearsI believe that stringByTrimmingCharactersInSet only trims the begin and end of the String
-
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 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 over 8 yearsnote: 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 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 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 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 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 usingcharacterSetWithCharactersInString
could be used with any characters, if you are worried. -
Iulian Onofrei over 7 yearsWhy is this page saying it's available from iOS 10+ if this answer in from 2011?
-
Jeff over 7 yearsWhy not just
NSCharacterSet *areDigits = [NSCharacterSet decimalDigitCharacterSet]
and then test for!= NSNotFound
? -
Biniou over 7 yearsWhat happens for a string with hundreds of characters all digits? Is there a limit to the NSNumber created?
-
gyratory circus about 7 years@Jeff You'd encounter false positives for strings that contain numerical characters, but do not represent numerical values, such as
g2g
-
Mathi Arasan almost 7 yearsI 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 errorPlayground execution aborted: error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
-
Mathi Arasan almost 7 yearsI don't know that correct one for swift 3. Correct me if I wrong.
-
Dannie P over 6 yearsClean and nice, best solution. I like that this does not do unnecessary
.inverted
and other actions. -
KM Gorbunov over 5 yearsinvertedSet What is the cost of that operation? It should be a huge NSCharacterSet.
-
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 over 5 yearsThis solution will accept a variety of likely-unexpected strings:
𝟷𝟸𝟹
,𝟙𝟚𝟛
,१२३
,١٢٣
, and১২৩
, to name a few. My answer, among others, addresses this issue. -
LimeRed about 5 yearsHow about just
return matchRange.location != NSNotFound;
-
Thomas over 4 yearswhat about the minus sign?
-
Thomas over 4 yearsthe 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 over 4 years@Thomas With
^-?\d+$
, I verified it on the site: regex101.com -
Alexandre Odet about 2 yearsSuper clean solution, really like it !