Averiguar si una cadena es numérica o no

93

¿Cómo podemos comprobar si una cadena está formada solo por números? Estoy sacando una subcadena de una cadena y quiero verificar si es una subcadena numérica o no.

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

Respuestas:

241

Aquí hay una forma que no depende de la precisión limitada de intentar analizar la cadena como un número:

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

Ver +[NSCharacterSet decimalDigitCharacterSet]y -[NSString rangeOfCharacterFromSet:].

John Calsbeek
fuente
6
esto es cierto para @ "231.123", por lo que: // newString consta solo de los dígitos del 0 al 9 y el .carácter
Nicolas Tyler
5
NSMutableCharacterSet * digitsAndDots = [NSMutableCharacterSet decimalDigitCharacterSet]; [digitsAndDots addCharactersInString: @ "."]; NSCharacterSet * notDigitsNorDots = [digitsAndDots invertedSet]; // también, gracias por traer "invertedSet". No sabía sobre su existencia
codrut
5
nota: este método considera que @ "" es un número; si corresponde, es posible que también deba verificar [newString lenght]> 0. Por favor, avíseme si me equivoco.
markckim
2
@Supertecnoboff Parece que esto es algo que ha cambiado en IOS. Esto se puede usar en su lugar para estar seguro:[[NSCharacterSet characterSetWithCharactersInString:@"0123456789."] invertedSet]]
Nicolas Tyler
2
@KMGorbunov No creo que NSCharacterSet esté almacenando un conjunto de respaldo que contenga todos los caracteres del conjunto. Sospecho que invertedSetestá devolviendo una clase que envuelve el conjunto en el que se creó y devuelve el valor opuesto para todas las consultas. Pero no lo sé con certeza.
John Calsbeek
33

Sugeriría usar el numberFromString:método de la clase NSNumberFormatter , como si el número no fuera válido, devolverá nil; de lo contrario, le devolverá un NSNumber.

NSNumberFormatter *nf = [[[NSNumberFormatter alloc] init] autorelease];
BOOL isDecimal = [nf numberFromString:newString] != nil;
Sam Symons
fuente
¿Está seguro de que no devolverá un número válido, por ejemplo, para @ "124sbd"?
Tommy
No, tanto NSNumberFormatter como NSScanner regresarían NOpara su cadena. También vale la pena saberlo: también regresan YESpara números rellenados con espacios en blanco. Por cierto, acabo de agregar un fragmento de código real a su respuesta, espero que no le importe.
Regexident
NSScanner debería devolver 'YES' para esa cadena, según la documentación, habiendo encontrado "una representación de entero válida". Además, hizo exactamente eso en una prueba en iOS 4.3.2. Sin embargo, NSNumberFormatter devolvió nil.
Tommy
Esto no atrapa '.' que es un requisito para mí. La respuesta de @John Calsbeek funcionó muy bien.
Damian
¿Qué sucede con una cadena con cientos de caracteres todos dígitos? ¿Existe un límite para el NSNumber creado?
Biniou
11

Validar por expresión regular, por patrón "^[0-9]+$", con el siguiente método -validateString:withPattern:.

[self validateString:"12345" withPattern:"^[0-9]+$"];
  1. Si se considera "123.123"
    • Con patrón "^[0-9]+(.{1}[0-9]+)?$"
  2. Si son exactamente números de 4 dígitos, sin ".".
    • Con patrón "^[0-9]{4}$".
  3. Si los números de dígitos no tienen ".", y la longitud está entre 2 ~ 5.
    • Con patrón "^[0-9]{2,5}$".
  4. Con signo menos: "^-?\d+$"

La expresión regular se puede comprobar en el sitio web en línea .

La función de ayuda es la siguiente.

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

Versión Swift 3:

Prueba en el patio de recreo.

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)
AechoLiu
fuente
Intenté así para 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 }Y recibí un errorPlayground execution aborted: error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
Mathi Arasan
No sé cuál es el correcto para Swift 3. Corrígeme si me equivoco.
Mathi Arasan
¿Qué tal simplementereturn matchRange.location != NSNotFound;
LimeRed
¿qué pasa con el signo menos?
Thomas
@Thomas With ^-?\d+$, lo verifiqué en el sitio: regex101.com
AechoLiu
9

Puede crear un NSScanner y simplemente escanear la cadena:

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

Consulte la documentación de NSScanner para conocer más métodos entre los que elegir.

Regexident
fuente
¿No te diría eso si al menos el primer carácter es numérico?
Tommy
Culpa mía. Olvidé la última llamada a isAtEnd.
Regexident
6

Creo que la forma más fácil de verificar que cada carácter dentro de una cadena dada sea numérico es probablemente:

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

Utilice uno de los otros métodos de fábrica de NSCharacterSet si desea un control total sobre los caracteres aceptables.

Tommy
fuente
Me gusta esta forma. Aunque no parece muy limpio, es una forma muy fácil y amigable para la base de verificar si hay "no dígitos" en un NSString. + Creo que es mucho más rápido que cualquier otra forma basada en robots (aunque probablemente no estemos analizando mucho el rendimiento aquí).
Nembleton
Creo que stringByTrimmingCharactersInSet solo recorta el comienzo y el final de String
Brody Robertson
@BrodyRobertson: lo hace. Lo que no importa en lo más mínimo para esta respuesta. ¿En qué ejemplo crees que fallaría esta prueba?
Tommy
@Tommy, Después de reevaluar, entiendo su respuesta y es válida y votaré, pero necesita que edite para permitirme volver a votar
Brody Robertson
6

Esta pregunta original era sobre Objective-C, pero también se publicó años antes de que se anunciara Swift. Entonces, si vienes aquí desde Google y estás buscando una solución que use Swift, aquí tienes:

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.")
}
Dos pajitas
fuente
6

Solución Swift 3 si necesita verificar que la cadena solo tiene dígitos:

CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: myString))
plamkata__
fuente
1
Limpio y agradable, la mejor solución. Me gusta que esto no haga .invertedacciones innecesarias y de otro tipo.
Dannie P
4

para ser claros, esto funciona para enteros en cadenas.

aquí hay una pequeña categoría de ayuda basada en la respuesta de John anterior:

en archivo .h

@interface NSString (NumberChecking)

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

@end

en archivo .m

#import "NSString+NumberChecking.h"

@implementation NSString (NumberChecking)

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

@end

uso:

#import "NSString+NumberChecking.h"

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

La solución Swift 3 podría ser como:

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)
        }
    }
}
hhamm
fuente
2

La respuesta de John Calsbeek es casi correcta pero omite algunos casos de borde Unicode.

Según la documentación dedecimalDigitCharacterSet , ese conjunto incluye todos los caracteres categorizados por Unicode como Nd. Así su respuesta aceptará, entre otros:

  • (U + 0967 DEVANAGARI DIGIT ONE)
  • (U + 1811 MONGOLIAN DIGIT ONE)
  • 𝟙 (U + 1D7D9 DÍGITO UNO MATEMÁTICO DE DOBLE CUERDA)

Si bien en cierto sentido esto es correcto, cada carácter en Ndse asigna a un dígito decimal, es casi seguro que no es lo que esperaba el autor de la pregunta. En el momento de escribir este artículo, hay 610 puntos de código categorizadosNd , de los cuales solo diez son los caracteres esperados 0(U + 0030) a 9(U + 0039).

Para solucionar el problema, simplemente especifique exactamente los caracteres que son aceptables:

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

Otra opción más:

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

Ejemplo de uso:

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

Una extensión de la respuesta de @John Calsbeek y una aclaración de los comentarios de @Jeff y @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;
}

Los siguientes pueden agregarse como métodos de categoría para 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;
}
Abdurrahman Mubeen Ali
fuente
0

Probar si una cadena es un número que podría ser útil

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

if (i == NSNotFound) {
     //is a number
}
Ashok Kumar
fuente
0

Extensión rápida:

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

Para Swift 3

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

Cuando tiene dígitos que son de idiomas mixtos , que usan (o no) los formatos de 0-9 dígitos, necesitará ejecutar una expresión regular que buscará cualquier número, lo siguiente es convertir todos los dígitos para que sean 0- 9 formato (si necesita el valor real):

// 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
}
OhadM
fuente
0

La forma más fácil y confiable es intentar lanzar como Double, si el resultado es nil, no se puede convertir en un número legítimo.

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]

Más información en la documentación: https://developer.apple.com/documentation/swift/double/2926277-init

Ariel
fuente
0

Simple escribió la extensión de la cadena:

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