Scoprire se una stringa è numerica o meno


93

Come possiamo verificare se una stringa è composta solo da numeri. Sto estraendo una sottostringa da una stringa e voglio controllare se è una sottostringa numerica o meno.

NSString *newString = [myString substringWithRange:NSMakeRange(2,3)];

Risposte:


241

Ecco un modo che non si basa sulla precisione limitata del tentativo di analizzare la stringa come numero:

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

Vedi +[NSCharacterSet decimalDigitCharacterSet]e -[NSString rangeOfCharacterFromSet:].


6
questo è vero per @ "231.123" quindi: // newString è composto solo dalle cifre da 0 a 9 e dal .carattere
Nicolas Tyler

5
NSMutableCharacterSet * digitsAndDots = [NSMutableCharacterSet decimalDigitCharacterSet]; [digitsAndDots addCharactersInString: @ "."]; NSCharacterSet * notDigitsNorDots = [digitsAndDots invertedSet]; // anche, grazie per aver introdotto "invertedSet". Non sapevo della sua esistenza
codrut

5
nota: questo metodo considera @ "" un numero; se applicabile, potrebbe essere necessario controllare anche [newString lenght]> 0. Per favore fatemi sapere se sbaglio.
Markckim

2
@Supertecnoboff Sembra che questo sia qualcosa che è cambiato in IOS. Questo può essere usato invece per essere sicuri:[[NSCharacterSet characterSetWithCharactersInString:@"0123456789."] invertedSet]]
Nicolas Tyler

2
@KMGorbunov Non penso che NSCharacterSet stia effettivamente memorizzando un set di supporto che contiene ogni singolo carattere del set. Sospetto che invertedSetstia restituendo una classe che avvolge il set in cui è stato creato e restituisce il valore opposto per tutte le query. Ma non lo so per certo.
John Calsbeek

33

Suggerirei di utilizzare il numberFromString:metodo della classe NSNumberFormatter , poiché se il numero non fosse valido, restituirà nil; in caso contrario, ti restituirà un NSNumber.

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

Sei sicuro che non restituirà un numero valido, ad esempio per @ "124sbd"?
Tommy

No, sia NSNumberFormatter che NSScanner restituiranno la NOtua stringa. Vale anche la pena sapere: tornano anche YESper i numeri riempiti con spazi bianchi. A proposito, ho appena aggiunto un frammento di codice effettivo alla tua risposta, spero non ti dispiaccia.
Regexident

NSScanner dovrebbe restituire "YES" per quella stringa, secondo la documentazione, dopo aver trovato "una rappresentazione intera valida". Inoltre, ha fatto esattamente questo in un test su iOS 4.3.2. Tuttavia, NSNumberFormatter ha restituito zero.
Tommy

Questo non cattura "." che è un requisito per me. La risposta di @John Calsbeek ha funzionato bene.
Damian

Cosa succede per una stringa con centinaia di caratteri tutte cifre? C'è un limite al NSNumber creato?
Biniou

11

Convalida tramite espressione regolare, modello "^[0-9]+$", con il seguente metodo -validateString:withPattern:.

[self validateString:"12345" withPattern:"^[0-9]+$"];
  1. Se si considera "123.123"
    • Con motivo "^[0-9]+(.{1}[0-9]+)?$"
  2. Se esattamente i numeri a 4 cifre, senza ".".
    • Con motivo "^[0-9]{4}$".
  3. Se la cifra è senza numeri "."e la lunghezza è compresa tra 2 e 5
    • Con motivo "^[0-9]{2,5}$".
  4. Con segno meno: "^-?\d+$"

L'espressione regolare può essere verificata nel sito web in linea .

La funzione di supporto è la seguente.

// 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;
}

Versione Swift 3:

Prova nel parco giochi.

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)

Ho provato in questo modo per 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 }E ho ricevuto un errorePlayground execution aborted: error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
Mathi Arasan

Non so quello corretto per swift 3. Correggimi se sbaglio.
Mathi Arasan

Che ne dici direturn matchRange.location != NSNotFound;
LimeRed

e il segno meno?
Thomas,

@Thomas With ^-?\d+$, l'ho verificato sul sito: regex101.com
AechoLiu

9

Puoi creare un NSScanner e scansionare semplicemente la stringa:

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

Consulta la documentazione di NSScanner per ulteriori metodi tra cui scegliere.


Non ti direbbe solo se almeno il primo carattere è numerico?
Tommy

Colpa mia. Hai dimenticato l'ultima chiamata a isAtEnd.
Regexident

6

Penso che il modo più semplice per verificare che ogni carattere all'interno di una determinata stringa sia numerico è probabilmente:

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");
}

Utilizzare uno degli altri metodi predefiniti NSCharacterSet se si desidera il controllo completo sui caratteri accettabili.


Mi piace così. Sebbene non sembri molto pulito, è un modo molto semplice e intuitivo per controllare se ci sono "non cifre" in una NSString. + Penso che sia molto più veloce di qualsiasi altro metodo basato su robot (anche se probabilmente non stiamo guardando molto alle prestazioni qui).
nembleton

Credo che stringByTrimmingCharactersInSet ritagli solo l'inizio e la fine della stringa
Brody Robertson

@BrodyRobertson: lo fa. Che non ha importanza per questa risposta. Per quale esempio pensi che questo test fallirebbe?
Tommy

@ Tommy, dopo aver rivalutato ho capito la tua risposta ed è valida e voterò in positivo, ma è necessario che tu modifichi per consentirmi di revote
Brody Robertson

6

Questa domanda originale riguardava Objective-C, ma è stata anche pubblicata anni prima dell'annuncio di Swift. Quindi, se vieni qui da Google e stai cercando una soluzione che utilizzi Swift, ecco qui:

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

6

Soluzione Swift 3 se è necessario verificare che la stringa contenga solo cifre:

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

1
Pulito e piacevole, la migliore soluzione. Mi piace che questo non faccia .invertedazioni inutili e di altro tipo.
Dannie P

4

per essere chiari, questo funziona per gli interi nelle stringhe.

ecco una piccola categoria di aiuto basata sulla risposta di John sopra:

nel file .h

@interface NSString (NumberChecking)

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

@end

nel file .m

#import "NSString+NumberChecking.h"

@implementation NSString (NumberChecking)

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

@end

utilizzo:

#import "NSString+NumberChecking.h"

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

4

La soluzione Swift 3 potrebbe essere come:

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

2

La risposta di John Calsbeek è quasi corretta ma omette alcuni casi limite Unicode.

Secondo la documentazione didecimalDigitCharacterSet , quel set include tutti i caratteri classificati da Unicode come Nd. Così la loro risposta accetterà, tra gli altri:

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

Anche se in un certo senso questo è corretto - ogni carattere in Ndviene mappato su una cifra decimale - quasi certamente non è quello che il richiedente si aspettava. Al momento della stesura di questo documento ci sono 610 punti di codice classificati comeNd , solo dieci dei quali sono i caratteri previsti 0(U + 0030) fino a 9(U + 0039).

Per risolvere il problema, è sufficiente specificare esattamente i caratteri accettabili:

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

1

Ancora un'altra opzione:

- (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;
    }
}

Esempio di utilizzo:

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

1

Un'estensione della @ John Calsbeek 'risposta s, e chiarire @Jeff e circo @gyratory ' s commenti.

+ (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;
}

Quanto segue può essere aggiunto come metodi di categoria per 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;
}

0

Verifica se una stringa è un numero Potrebbe essere utile

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

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

0

Estensione rapida:

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

0

Per Swift 3

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

0

Quando hai cifre che provengono da lingue miste , che usano (o non usano) i formati 0-9 cifre, dovrai eseguire una regex che cercherà qualsiasi numero, la prossima cosa è convertire tutte le cifre in 0- Formato 9 (se è necessario il valore effettivo):

// 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
}

0

Il modo più semplice e affidabile è provare a lanciare come Double, se il risultato è nil- non può essere formato in un numero legittimo.

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]

Maggiori informazioni nella documentazione: https://developer.apple.com/documentation/swift/double/2926277-init


0

Simple ha scritto l'estensione della stringa:

extension String {
    var isNumeric: Bool {
        return self.rangeOfCharacter(from: CharacterSet.decimalDigits.inverted) == nil
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.