Descobrir se uma string é numérica ou não

93

Como podemos verificar se uma string é composta apenas por números. Estou tirando uma substring de uma string e quero verificar se é uma substring numérica ou não.

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

Respostas:

241

Esta é uma maneira que não depende da precisão limitada de tentar analisar a string como um número:

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

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

John Calsbeek
fonte
6
isso é verdadeiro para @ "231.123" então: // newString consiste apenas nos dígitos de 0 a 9 e no .caractere
Nicolas Tyler
5
NSMutableCharacterSet * digitsAndDots = [NSMutableCharacterSet decimalDigitCharacterSet]; [digitsAndDots addCharactersInString: @ "."]; NSCharacterSet * notDigitsNorDots = [digitsAndDots invertedSet]; // também, obrigado por trazer "invertedSet". Eu não sabia sobre sua existência
codrut de
5
nota: este método considera @ "" um número; se aplicável, pode ser necessário verificar também se [newString lenght]> 0. informe-me se eu estiver errado.
markckim
2
@Supertecnoboff Parece que isso é algo que mudou no IOS. Isso pode ser usado para garantir:[[NSCharacterSet characterSetWithCharactersInString:@"0123456789."] invertedSet]]
Nicolas Tyler,
2
@KMGorbunov Não acho que NSCharacterSet esteja realmente armazenando um conjunto de apoio que contém todos os caracteres do conjunto. Suspeito que invertedSetestá retornando uma classe que envolve o formulário de conjunto em que foi criado e retorna o valor oposto para todas as consultas. Mas não tenho certeza.
John Calsbeek
33

Sugiro usar o numberFromString:método da classe NSNumberFormatter , pois se o número não for válido, ele retornará nulo; caso contrário, ele retornará um NSNumber.

NSNumberFormatter *nf = [[[NSNumberFormatter alloc] init] autorelease];
BOOL isDecimal = [nf numberFromString:newString] != nil;
Sam Symons
fonte
Tem certeza de que não retornará um número válido, por exemplo, para @ "124sbd"?
Tommy
Não, NSNumberFormatter e NSScanner retornariam NOpara sua string. Também vale a pena saber: eles também retornam YESpara números preenchidos com espaços em branco. A propósito, acabei de adicionar algum trecho de código real à sua resposta, espero que você não se importe.
Regexident de
O NSScanner deve retornar 'SIM' para aquela string, de acordo com a documentação, tendo encontrado "uma representação de inteiro válida". Além disso, fez exatamente isso em um teste no iOS 4.3.2. No entanto, NSNumberFormatter retornou nulo.
Tommy
Isso não pega '.' que é um requisito para mim. A resposta de @John Calsbeek funcionou bem.
Damian
O que acontece com uma string com centenas de caracteres todos dígitos? Existe um limite para o NSNumber criado?
Biniou
11

Valide por expressão regular, por padrão "^[0-9]+$", com o método a seguir -validateString:withPattern:.

[self validateString:"12345" withPattern:"^[0-9]+$"];
  1. Se "123.123" for considerado
    • Com padrão "^[0-9]+(.{1}[0-9]+)?$"
  2. Se exatamente números de 4 dígitos, sem ".".
    • Com padrão "^[0-9]{4}$".
  3. Se os dígitos não forem números ".", e o comprimento estiver entre 2 ~ 5
    • Com padrão "^[0-9]{2,5}$".
  4. Com sinal de menos: "^-?\d+$"

A expressão regular pode ser verificada no site online .

A função auxiliar é a seguinte.

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

Versão Swift 3:

Teste no 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)
AechoLiu
fonte
Tentei assim para o 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 obtive o erroPlayground execution aborted: error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
Mathi Arasan
Não sei aquele correto para o rápido 3. Corrija-me se eu estiver errado.
Mathi Arasan
Que tal apenasreturn matchRange.location != NSNotFound;
LimeRed
e quanto ao sinal de menos?
Thomas
@Thomas With ^-?\d+$, eu verifiquei no site: regex101.com
AechoLiu
9

Você pode criar um NSScanner e simplesmente escanear a string:

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

Verifique a documentação do NSScanner para mais métodos para escolher.

Regexident
fonte
Isso não diria se pelo menos o primeiro caractere é numérico?
Tommy
Foi mal. Esqueci a ligação final para isAtEnd.
Regexident de
6

Acho que a maneira mais fácil de verificar se todos os caracteres em uma determinada string são numéricos é provavelmente:

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 um dos outros métodos de fábrica NSCharacterSet se desejar controle total sobre os caracteres aceitáveis.

Tommy
fonte
Eu gosto desse jeito. Embora não pareça muito claro, é uma maneira muito fácil e amigável para a base de verificar se há "não dígitos" em um NSString. + Eu acho que é muito mais rápido do que qualquer outra forma baseada em robô (embora provavelmente não estejamos olhando muito para o desempenho aqui).
nembleton
Eu acredito que stringByTrimmingCharactersInSet apenas apara o início e o final da String
Brody Robertson
@BrodyRobertson: sim. O que não importa nem um pouco para esta resposta. Em que exemplo você acha que este teste falharia?
Tommy
@Tommy, depois de reavaliar eu entendo sua resposta e ela é válida e votará positivamente, mas você precisa que você edite para permitir que eu revote
Brody Robertson
6

Esta pergunta original era sobre Objective-C, mas também foi postada anos antes do anúncio do Swift. Então, se você está vindo do Google e está procurando uma solução que usa Swift, aqui está:

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

Solução Swift 3 se necessário verificar se a string tem apenas dígitos:

CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: myString))
plamkata__
fonte
1
Limpo e agradável, a melhor solução. Eu gosto que isso não faça .invertedações desnecessárias e outras.
Dannie P
4

para ficar claro, isso funciona para inteiros em strings.

aqui está uma pequena categoria de ajudante baseada na resposta de John acima:

no arquivo .h

@interface NSString (NumberChecking)

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

@end

em arquivo .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
fonte
4

A solução do Swift 3 pode 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
fonte
2

A resposta de John Calsbeek está quase correta, mas omite alguns casos extremos do Unicode.

De acordo com a documentação dedecimalDigitCharacterSet , esse conjunto inclui todos os caracteres categorizados por Unicode como Nd. Assim, sua resposta aceitará, entre outras:

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

Embora em certo sentido isso seja correto - cada caractere em Ndmapeia para um dígito decimal - quase certamente não é o que o autor da pergunta esperava. No momento em que este livro foi escrito, havia 610 pontos de código categorizados comoNd , apenas dez dos quais são os caracteres esperados 0(U + 0030) a 9(U + 0039).

Para corrigir o problema, basta especificar exatamente os caracteres que são aceitáveis:

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

Ainda outra opção:

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

Exemplo de uso:

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

Uma extensão da resposta de @John Calsbeek e esclarecimento dos comentários de @Jeff e @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;
}

O seguinte pode ser adicionado como métodos de categoria 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
fonte
0

Teste se uma string é um número que pode ser útil

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

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

Extensão 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
}  }
Bhavesh Patel
fonte
0

Para Swift 3

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

Quando você tem dígitos de idiomas mistos , que usam (ou não) os formatos de 0-9 dígitos, você precisará executar uma regex que irá procurar por qualquer número, a próxima coisa é converter todos os dígitos para 0- Formato 9 (se você precisar do 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
fonte
0

A maneira mais fácil e confiável é tentar lançar como Double, se o resultado for nil- não pode ser formado em um 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]

Mais informações na documentação: https://developer.apple.com/documentation/swift/double/2926277-init

Ariel
fonte
0

Simple escreveu a extensão da string:

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