Savoir si une chaîne est numérique ou non


93

Comment pouvons-nous vérifier si une chaîne est composée uniquement de nombres. Je retire une sous-chaîne d'une chaîne et je veux vérifier si c'est une sous-chaîne numérique ou non.

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

Réponses:


241

Voici une façon qui ne repose pas sur la précision limitée de la tentative d'analyse de la chaîne en tant que nombre:

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

Voir +[NSCharacterSet decimalDigitCharacterSet]et -[NSString rangeOfCharacterFromSet:].


6
ceci est vrai pour @ "231.123" donc: // newString se compose uniquement des chiffres de 0 à 9 et du .caractère
Nicolas Tyler

5
NSMutableCharacterSet * digitsAndDots = [NSMutableCharacterSet decimalDigitCharacterSet]; [digitsAndDots addCharactersInString: @ "."]; NSCharacterSet * notDigitsNorDots = [digitsAndDots invertedSet]; // aussi, merci d'avoir introduit "invertedSet". Je ne savais pas son existence
codrut

5
note: cette méthode considère @ "" comme un nombre; le cas échéant, vous devrez peut-être également vérifier [newStringlenght]> 0. s'il vous plaît laissez-moi savoir si je me trompe.
markckim

2
@Supertecnoboff Il semble que ce soit quelque chose qui a changé dans IOS. Cela peut être utilisé à la place pour être sûr:[[NSCharacterSet characterSetWithCharactersInString:@"0123456789."] invertedSet]]
Nicolas Tyler

2
@KMGorbunov Je ne pense pas que NSCharacterSet stocke réellement un jeu de sauvegarde contenant chaque caractère de l'ensemble. Je soupçonne qu'il invertedSetrenvoie une classe qui enveloppe l'ensemble sous sa forme créée et renvoie la valeur opposée pour toutes les requêtes. Mais je ne sais pas avec certitude.
John Calsbeek

33

Je suggère d'utiliser la numberFromString:méthode de la classe NSNumberFormatter , comme si le nombre n'est pas valide, il renverra nil; sinon, il vous renverra un NSNumber.

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

Êtes-vous sûr qu'il ne renverra pas de numéro valide, par exemple pour @ "124sbd"?
Tommy

Non, NSNumberFormatter et NSScanner renverraient NOvotre chaîne. A savoir également: ils reviennent également YESpour des nombres remplis d'espaces. Btw, je viens d'ajouter un extrait de code à votre réponse, j'espère que cela ne vous dérange pas.
Regexident

NSScanner devrait retourner «OUI» pour cette chaîne, selon la documentation, après avoir trouvé «une représentation entière valide». De plus, c'est exactement ce qu'il a fait lors d'un test sur iOS 4.3.2. Cependant, NSNumberFormatter a renvoyé nil.
Tommy

Cela n'attrape pas '.' ce qui est une exigence pour moi. La réponse de @John Calsbeek a bien fonctionné.
Damian

Que se passe-t-il pour une chaîne de centaines de caractères tous chiffres? Y a-t-il une limite au NSNumber créé?
Biniou

11

Validez par expression régulière, par modèle "^[0-9]+$", avec la méthode suivante -validateString:withPattern:.

[self validateString:"12345" withPattern:"^[0-9]+$"];
  1. Si "123.123" est considéré
    • Avec motif "^[0-9]+(.{1}[0-9]+)?$"
  2. Si exactement 4 chiffres, sans "." .
    • Avec motif "^[0-9]{4}$".
  3. Si les chiffres sans "." et que la longueur est comprise entre 2 et 5.
    • Avec motif "^[0-9]{2,5}$".
  4. Avec signe moins: "^-?\d+$"

L'expression régulière peut être vérifiée sur le site Web en ligne .

La fonction d'assistance est la suivante.

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

Version Swift 3:

Test dans la cour de récréation.

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)

J'ai essayé comme ça pour 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 }Et j'ai eu une erreurPlayground execution aborted: error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
Mathi Arasan

Je ne connais pas celui qui est correct pour Swift 3. Corrigez-moi si je me trompe.
Mathi Arasan

Que diriez-vous justereturn matchRange.location != NSNotFound;
LimeRed

qu'en est-il du signe moins?
Thomas le

@Thomas With ^-?\d+$, je l'ai vérifié sur le site: regex101.com
AechoLiu

9

Vous pouvez créer un NSScanner et scanner simplement la chaîne:

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

Consultez la documentation de NSScanner pour plus de méthodes à choisir.


Cela ne vous dirait-il pas simplement si au moins le premier caractère est numérique?
Tommy

Ma faute. J'ai oublié le dernier appel àisAtEnd .
Regexident

6

Je pense que le moyen le plus simple de vérifier que chaque caractère dans une chaîne donnée est numérique est probablement:

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

Utilisez l'une des autres méthodes de fabrique NSCharacterSet si vous voulez un contrôle complet sur les caractères acceptables.


J'aime cette façon. Bien que cela ne semble pas très clair, c'est une manière très simple et conviviale de vérifier s'il y a des "non-chiffres" dans une NSString. + Je pense que c'est beaucoup plus rapide que tout autre moyen basé sur un robot (bien que nous ne cherchions probablement pas beaucoup les performances ici).
nembleton

Je crois que stringByTrimmingCharactersInSet coupe uniquement le début et la fin de la chaîne
Brody Robertson

@BrodyRobertson: c'est le cas. Ce qui importe pas du tout pour cette réponse. Pour quel exemple pensez-vous que ce test échouerait?
Tommy

@Tommy, après avoir réévalué, je comprends votre réponse et elle est valide et votera pour mais vous devez modifier pour me permettre de revenir
Brody Robertson

6

Cette question originale concernait Objective-C, mais elle a également été publiée des années avant l'annonce de Swift. Donc, si vous venez ici de Google et que vous recherchez une solution qui utilise Swift, vous y allez:

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

Solution Swift 3 si besoin de vérifier que la chaîne ne comporte que des chiffres:

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

1
Propre et agréable, meilleure solution. J'aime que cela ne fasse pas .invertedd'actions inutiles et autres.
Dannie P

4

pour être clair, cela fonctionne pour les entiers dans les chaînes.

Voici une petite catégorie d'aide basée sur la réponse de John ci-dessus:

dans un fichier .h

@interface NSString (NumberChecking)

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

@end

dans un fichier .m

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

4

La solution Swift 3 pourrait être comme:

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 réponse de John Calsbeek est presque correcte mais omet certains cas de bord Unicode.

Selon la documentation dedecimalDigitCharacterSet , cet ensemble comprend tous les caractères classés par Unicode comme Nd. Ainsi leur réponse acceptera, entre autres:

  • (U + 0967 DEVANAGARI DIGIT ONE)
  • (U + 1811 CHIFFRE MONGOLIEN UN)
  • 𝟙 (U + 1D7D9 CHIFFRE MATHÉMATIQUE À DOUBLE EFFET UN)

Bien que dans un certain sens, cela soit correct - chaque caractère dans Ndcorrespond à un chiffre décimal - ce n'est certainement pas ce à quoi le demandeur s'attendait. Au moment de la rédaction de cet article, il existe 610 points de code classés commeNd , dont seulement dix sont les caractères attendus 0(U + 0030) à 9(U + 0039).

Pour résoudre le problème, spécifiez simplement exactement les caractères acceptables:

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

1

Encore une autre 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;
    }
}

Exemple d'utilisation:

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

1

Une extension de la réponse de @John Calsbeek , et une clarification des commentaires de @Jeff et @gyratory circus .

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

Les éléments suivants peuvent être ajoutés comme méthodes de catégorie pour 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

Tester si une chaîne est un nombre peut être utile

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

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

0

Extension Swift:

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

Pour Swift 3

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

0

Lorsque vous avez des chiffres issus de langues mixtes , qui utilisent (ou non) les formats de chiffres 0-9, vous devrez exécuter une expression régulière qui recherchera n'importe quel nombre, la prochaine chose à faire est de convertir tous les chiffres en 0- 9 format (si vous avez besoin de la valeur réelle):

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

Le moyen le plus simple et le plus fiable est d'essayer de lancer comme Double, si le résultat est nil- il ne peut pas être formé en un nombre légitime.

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]

Plus d'informations dans la documentation: https://developer.apple.com/documentation/swift/double/2926277-init


0

Simple a écrit l'extension de chaîne:

extension String {
    var isNumeric: Bool {
        return self.rangeOfCharacter(from: CharacterSet.decimalDigits.inverted) == nil
    }
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.