Sprawdzanie, czy ciąg jest numeryczny, czy nie


93

Jak możemy sprawdzić, czy ciąg składa się tylko z liczb. Pobieram podciąg z ciągu i chcę sprawdzić, czy jest to podciąg numeryczny, czy nie.

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

Odpowiedzi:


241

Oto jeden sposób, który nie polega na ograniczonej precyzji próby przeanalizowania ciągu jako liczby:

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

Zobacz +[NSCharacterSet decimalDigitCharacterSet]i -[NSString rangeOfCharacterFromSet:].


6
tak jest w przypadku @ "231.123", więc: // newString składa się tylko z cyfr od 0 do 9 i .znaku
— Nicolas Tyler

5
NSMutableCharacterSet * digitsAndDots = [NSMutableCharacterSet decimalDigitCharacterSet]; [digitsAndDots addCharactersInString: @ "."]; NSCharacterSet * notDigitsNorDots = [digitsAndDots invertedSet]; // także dzięki za wprowadzenie "invertedSet". Nie wiedziałem o jego istnieniu
— kodrut

5
uwaga: ta metoda traktuje @ "" jako liczbę; jeśli ma to zastosowanie, może być konieczne sprawdzenie, czy [newString lenght]> 0. Daj mi znać, jeśli się mylę.
— markckim

2
@Supertecnoboff Wygląda na to, że to się zmieniło w IOS. Zamiast tego można to wykorzystać, aby mieć pewność:[[NSCharacterSet characterSetWithCharactersInString:@"0123456789."] invertedSet]]
— Nicolas Tyler

2
@KMGorbunov Nie sądzę, aby zestaw NSCharacterSet faktycznie przechował zestaw kopii zapasowych, który zawiera każdy pojedynczy znak w zestawie. Podejrzewam, że invertedSetzwraca klasę, która zawija zestaw, w którym została utworzona, i zwraca przeciwną wartość dla wszystkich zapytań. Ale nie wiem na pewno.
— John Calsbeek

33

Sugerowałbym użycie numberFromString:metody z klasy NSNumberFormatter , tak jakby numer był niepoprawny, zwróci nil; w przeciwnym razie zwróci Ci NSNumber.

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

Czy na pewno nie zwróci prawidłowej liczby, np. Dla @ "124sbd"?
— Tommy

Nie, zarówno NSNumberFormatter, jak i NSScanner zwróciłyby NOTwój ciąg. Warto również wiedzieć: powracają również YESpo liczbach wypełnionych spacjami. Przy okazji, właśnie dodałem fragment kodu do Twojej odpowiedzi, mam nadzieję, że nie masz nic przeciwko.
— Regexident

NSScanner powinien zwrócić „TAK” dla tego ciągu, zgodnie z dokumentacją, po znalezieniu „prawidłowej reprezentacji liczby całkowitej”. Co więcej, zrobił to dokładnie w teście na iOS 4.3.2. Jednak NSNumberFormatter zwrócił nil.
— Tommy

To się nie sprawdza ”. co jest dla mnie wymagane. Odpowiedź użytkownika @John Calsbeek działała ładnie.
— Damian

Co dzieje się z łańcuchem zawierającym setki znaków, wszystkie cyfry? Czy istnieje limit utworzonego numeru NSNumber?
— Biniou

12

Sprawdź poprawność za pomocą wyrażenia regularnego, według wzorca "^[0-9]+$", przy użyciu następującej metody -validateString:withPattern:.

[self validateString:"12345" withPattern:"^[0-9]+$"];
  1. Jeśli brana jest pod uwagę wartość „123.123”
    • Z wzorem "^[0-9]+(.{1}[0-9]+)?$"
  2. Jeśli dokładnie 4 cyfry, bez ".".
    • Z wzorem "^[0-9]{4}$".
  3. Jeśli cyfry są bez ".", a długość wynosi od 2 do 5.
    • Z wzorem "^[0-9]{2,5}$".
  4. Ze znakiem minus: "^-?\d+$"

Wyrażenie regularne można sprawdzić w witrynie internetowej online .

Funkcja pomocnicza jest następująca.

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

Wersja Swift 3:

Przetestuj na placu zabaw.

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)

Próbowałem w ten sposób dla 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 }I otrzymałem błądPlayground execution aborted: error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
— Mathi Arasan

Nie znam tego właściwego dla szybkiego 3. Popraw mnie, jeśli się mylę.
— Mathi Arasan

Co powiesz na toreturn matchRange.location != NSNotFound;
— LimeRed

a co ze znakiem minus?
— Thomas

@Thomas With ^-?\d+$, zweryfikowałem to na stronie: regex101.com
— AechoLiu

9

Możesz utworzyć NSScanner i po prostu zeskanować ciąg:

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

Sprawdź dokumentację NSScanner, aby uzyskać więcej metod do wyboru.


Czy to nie mówi ci tylko, czy przynajmniej pierwszy znak jest liczbą?
— Tommy

Mój błąd. Zapomniałem o ostatnim telefonie do isAtEnd.
— Regexident

6

Myślę, że najłatwiejszym sposobem sprawdzenia, czy każdy znak w danym ciągu jest numeryczny, jest prawdopodobnie:

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

Użyj jednej z innych metod fabryki NSCharacterSet, jeśli chcesz mieć pełną kontrolę nad dopuszczalnymi znakami.


Podoba mi się ten sposób. Chociaż nie wydaje się to zbyt przejrzyste, jest to bardzo łatwy i przyjazny dla fundamentów sposób sprawdzania, czy w NSString znajdują się „niecyfrowe”. + Myślę, że jest znacznie szybszy niż jakikolwiek inny sposób oparty na robotach (chociaż prawdopodobnie nie patrzymy tutaj zbytnio na wydajność).
— nembleton

Uważam, że stringByTrimmingCharactersInSet przycina tylko początek i koniec ciągu
— Brody Robertson

@BrodyRobertson: tak. Nie ma to najmniejszego znaczenia dla tej odpowiedzi. Jak myślisz, na jakim przykładzie ten test się nie powiedzie?
— Tommy

@Tommy, Po ponownej ocenie, rozumiem twoją odpowiedź, jest ona prawidłowa i zagłosuję za nią, ale musisz ją edytować, aby umożliwić mi cofnięcie
— Brody Robertson

6

To pierwotne pytanie dotyczyło Objective-C, ale zostało również opublikowane wiele lat przed ogłoszeniem Swift. Jeśli więc przyjeżdżasz tutaj z Google i szukasz rozwiązania wykorzystującego Swift, proszę bardzo:

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

Rozwiązanie Swift 3, jeśli chcesz sprawdzić, czy ciąg ma tylko cyfry:

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

1
Czysto i przyjemnie, najlepsze rozwiązanie. Podoba mi się, że to nie robi zbędnych .invertedi innych działań.
— Dannie P,

4

aby było jasne, to działa dla liczb całkowitych w łańcuchach.

Oto mała kategoria pomocnicza oparta na odpowiedzi Johna powyżej:

w pliku h

@interface NSString (NumberChecking)

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

@end

w pliku m

#import "NSString+NumberChecking.h"

@implementation NSString (NumberChecking)

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

@end

stosowanie:

#import "NSString+NumberChecking.h"

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

4

Rozwiązanie Swift 3 mogłoby wyglądać następująco:

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

Rozszerzenie odpowiedzi @John Calsbeek i wyjaśnienie komentarzy @Jeff i @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;
}

Poniższe można dodać jako metody kategorii dla 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;
}

2

Odpowiedź Johna Calsbeeka jest prawie poprawna, ale pomija niektóre przypadki Unicode.

Zgodnie z dokumentacjądecimalDigitCharacterSet , ten zestaw zawiera wszystkie znaki sklasyfikowane przez Unicode jako Nd. W ten sposób ich odpowiedź przyjmie m.in .:

  • १ (U + 0967 DEVANAGARI DIGIT ONE)
  • ᠑ (U + 1811 MONGOLIAN DIGIT ONE)
  • 𝟙 (U + 1D7D9 MATEMATYCZNA PODWÓJNA CYFROWA CYFRA)

Chociaż w pewnym sensie jest to poprawne - każdy znak Ndodwzorowuje cyfrę dziesiętną - prawie na pewno nie jest to to, czego oczekiwał pytający. W chwili pisania tego tekstu istnieje 610 punktów kodowych sklasyfikowanych jakoNd , z których tylko dziesięć to oczekiwane znaki 0(od U ​​+ 0030) do9 (U + 0039).

Aby rozwiązać problem, po prostu określ dokładnie te znaki, które są dopuszczalne:

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

1

Jeszcze inna opcja:

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

Przykład użycia:

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

0

Sprawdź, czy ciąg jest liczbą. Może być pomocny

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

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

0

Szybkie rozszerzenie:

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

Dla Swift 3

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

0

Jeśli masz cyfry z języków mieszanych , które używają (lub nie) formatów cyfr 0-9, będziesz musiał uruchomić wyrażenie regularne, które będzie szukało dowolnej liczby, a następnie przekonwertuj wszystkie cyfry na 0- 9 format (jeśli potrzebujesz rzeczywistej wartości):

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

Najłatwiejszym i najbardziej niezawodnym sposobem jest próba rzucenia, ponieważ Doublejeśli wynik jest nil- nie można go uformować w legalną liczbę.

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]

Więcej informacji w dokumentacji: https://developer.apple.com/documentation/swift/double/2926277-init


0

Simple napisał rozszerzenie ciągu:

extension String {
    var isNumeric: Bool {
        return self.rangeOfCharacter(from: CharacterSet.decimalDigits.inverted) == nil
    }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.