Herausfinden, ob eine Zeichenfolge numerisch ist oder nicht

93

Wie können wir überprüfen, ob eine Zeichenfolge nur aus Zahlen besteht? Ich nehme einen Teilstring aus einem String heraus und möchte überprüfen, ob es sich um einen numerischen Teilstring handelt oder nicht.

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

Antworten:

241

Hier ist eine Möglichkeit, die nicht auf der begrenzten Genauigkeit des Versuchs beruht, die Zeichenfolge als Zahl zu analysieren:

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

Siehe +[NSCharacterSet decimalDigitCharacterSet]und -[NSString rangeOfCharacterFromSet:].

John Calsbeek
quelle
6
Dies gilt für @ "231.123", also: // newString besteht nur aus den Ziffern 0 bis 9 und dem .Zeichen
Nicolas Tyler
5
NSMutableCharacterSet * digitsAndDots = [NSMutableCharacterSet decimalDigitCharacterSet]; [digitsAndDots addCharactersInString: @ "."]; NSCharacterSet * notDigitsNorDots = [digitsAndDots invertedSet]; // Danke auch für das Einbringen von "invertedSet". Ich wusste nichts über seine Existenz
Codrut
5
Hinweis: Bei dieser Methode wird @ "" als Zahl betrachtet. Falls zutreffend, müssen Sie möglicherweise auch nach [newString lenght]> 0 suchen. Bitte lassen Sie mich wissen, wenn ich falsch liege.
Markckim
2
@ Supertecnoboff Es scheint, dass sich dies in IOS geändert hat. Dies kann stattdessen verwendet werden, um sicher zu sein:[[NSCharacterSet characterSetWithCharactersInString:@"0123456789."] invertedSet]]
Nicolas Tyler
2
@KMGorbunov Ich glaube nicht, dass NSCharacterSet tatsächlich einen Hintergrundsatz speichert, der jedes einzelne Zeichen im Satz enthält. Ich vermute invertedSet, dass eine Klasse zurückgegeben wird, die den Satz umschließt, von dem sie erstellt wurde, und den entgegengesetzten Wert für alle Abfragen zurückgibt. Aber ich weiß es nicht genau.
John Calsbeek
33

Ich würde vorschlagen, die numberFromString:Methode aus der NSNumberFormatter- Klasse zu verwenden. Wenn die Zahl nicht gültig ist, wird nil zurückgegeben. Andernfalls erhalten Sie eine NS-Nummer.

NSNumberFormatter *nf = [[[NSNumberFormatter alloc] init] autorelease];
BOOL isDecimal = [nf numberFromString:newString] != nil;
Sam Symons
quelle
Sind Sie sicher, dass keine gültige Nummer zurückgegeben wird, z. B. für @ "124sbd"?
Tommy
Nein, sowohl NSNumberFormatter als auch NSScanner würden NOfür Ihre Zeichenfolge zurückkehren. Ebenfalls wissenswert: Sie kehren auch YESfür mit Leerzeichen aufgefüllte Zahlen zurück. Übrigens habe ich Ihrer Antwort gerade ein aktuelles Code-Snippet hinzugefügt, hoffe, es macht Ihnen nichts aus.
Regexident
NSScanner sollte laut Dokumentation 'YES' für diese Zeichenfolge zurückgeben, nachdem "eine gültige Ganzzahldarstellung" gefunden wurde. Darüber hinaus hat es genau das in einem Test unter iOS 4.3.2 getan. NSNumberFormatter hat jedoch null zurückgegeben.
Tommy
Das fängt nicht '.' Das ist eine Voraussetzung für mich. Antwort von @John Calsbeek hat gut funktioniert.
Damian
Was passiert für eine Zeichenfolge mit Hunderten von Ziffern? Gibt es ein Limit für die erstellte NSNumber?
Biniou
11

Validieren Sie durch regulären Ausdruck, nach Muster "^[0-9]+$"mit der folgenden Methode -validateString:withPattern:.

[self validateString:"12345" withPattern:"^[0-9]+$"];
  1. Wenn "123.123" berücksichtigt wird
    • Mit Muster "^[0-9]+(.{1}[0-9]+)?$"
  2. Wenn genau 4-stellige Zahlen, ohne ".".
    • Mit Muster "^[0-9]{4}$".
  3. Wenn Ziffern ohne "."und die Länge zwischen 2 ~ 5 liegt.
    • Mit Muster "^[0-9]{2,5}$".
  4. Mit Minuszeichen: "^-?\d+$"

Der reguläre Ausdruck kann auf der Online-Website überprüft werden .

Die Hilfsfunktion ist wie folgt.

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

Swift 3 Version:

Test auf dem Spielplatz.

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
quelle
Ich habe es für Swift 3 so versucht. 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 }Und ich habe einen Fehler bekommenPlayground execution aborted: error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
Mathi Arasan
Ich kenne das richtige für Swift 3 nicht. Korrigieren Sie mich, wenn ich falsch liege.
Mathi Arasan
Wie wäre es nurreturn matchRange.location != NSNotFound;
LimeRed
Was ist mit dem Minuszeichen?
Thomas
@Thomas With ^-?\d+$, ich habe es auf der Website überprüft: regex101.com
AechoLiu
9

Sie können einen NSScanner erstellen und einfach die Zeichenfolge scannen:

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

Schauen Sie sich NSScanner Dokumentation für weitere Methoden zur Auswahl.

Regexident
quelle
Würde Ihnen das nicht einfach sagen, ob mindestens das erste Zeichen numerisch ist?
Tommy
Mein Fehler. Ich habe den letzten Anruf bei vergessen isAtEnd.
Regexident
6

Ich denke, der einfachste Weg, um zu überprüfen, ob jedes Zeichen in einer bestimmten Zeichenfolge numerisch ist, ist wahrscheinlich:

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

Verwenden Sie eine der anderen NSCharacterSet-Factory-Methoden, wenn Sie die vollständige Kontrolle über akzeptable Zeichen wünschen.

Tommy
quelle
Ich mag diesen Weg. Obwohl es nicht sehr sauber zu sein scheint, ist es eine sehr einfache und grundlagenfreundliche Methode, um zu überprüfen, ob ein NSString "nicht-Ziffern" enthält. + Ich denke, dass es viel schneller ist als alle anderen roboterbasierten Methoden (obwohl wir hier wahrscheinlich nicht viel auf Leistung achten).
Nembleton
Ich glaube, dass stringByTrimmingCharactersInSet nur den Anfang und das Ende des Strings schneidet
Brody Robertson
@BrodyRobertson: das tut es. Was für diese Antwort nicht im geringsten von Bedeutung ist. Für welches Beispiel würde dieser Test Ihrer Meinung nach fehlschlagen?
Tommy
@ Tommy, nach einer Neubewertung verstehe ich Ihre Antwort und sie ist gültig und wird positiv bewertet, aber Sie müssen sie bearbeiten, damit ich sie erneut bewerten kann
Brody Robertson
6

Diese ursprüngliche Frage betraf Objective-C, wurde aber auch Jahre vor der Ankündigung von Swift veröffentlicht. Wenn Sie also von Google hierher kommen und nach einer Lösung suchen, die Swift verwendet, können Sie Folgendes tun:

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
quelle
6

Swift 3-Lösung, wenn überprüft werden muss, ob die Zeichenfolge nur Ziffern enthält:

CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: myString))
plamkata__
quelle
1
Sauber und schön, beste Lösung. Ich mag es, dass dies keine unnötigen .invertedund anderen Aktionen macht.
Dannie P
4

Dies funktioniert für Ganzzahlen in Zeichenfolgen.

Hier ist eine kleine Hilfskategorie basierend auf Johns Antwort oben:

in der .h-Datei

@interface NSString (NumberChecking)

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

@end

in .m Datei

#import "NSString+NumberChecking.h"

@implementation NSString (NumberChecking)

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

@end

Verwendung:

#import "NSString+NumberChecking.h"

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

Die Swift 3-Lösung könnte wie folgt aussehen:

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
quelle
2

Die Antwort von John Calsbeek ist fast richtig, lässt jedoch einige Unicode- Randfälle aus .

Gemäß der Dokumentation für enthältdecimalDigitCharacterSet dieser Satz alle Zeichen, die von Unicode als kategorisiert wurden Nd. So wird ihre Antwort unter anderem akzeptieren:

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

Während dies in gewissem Sinne richtig ist - jedes Zeichen in Ndwird einer Dezimalstelle zugeordnet -, ist es mit ziemlicher Sicherheit nicht das, was der Fragesteller erwartet hat. Zum jetzigen Zeitpunkt gibt es 610 Codepunkte, die als kategorisiert sindNd , von denen nur zehn die erwarteten Zeichen 0(U + 0030) bis 9(U + 0039) sind.

Um das Problem zu beheben, geben Sie einfach genau die Zeichen an, die akzeptabel sind:

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

Noch eine 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;
    }
}

Anwendungsbeispiel:

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

Eine Erweiterung der Antwort von @John Calsbeek und eine Klarstellung der Kommentare von @Jeff und @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;
}

Folgendes kann als Kategoriemethode für hinzugefügt werden 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
quelle
0

Testen Sie, ob eine Zeichenfolge eine Zahl ist

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

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

Schnelle Erweiterung:

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
quelle
0

Für Swift 3

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

Wenn Sie Ziffern aus gemischten Sprachen haben , die die 0-9-Ziffernformate verwenden (oder nicht), müssen Sie einen regulären Ausdruck ausführen, der nach einer beliebigen Zahl sucht. Als Nächstes müssen Sie alle Ziffern in 0- konvertieren. 9-Format (wenn Sie den tatsächlichen Wert benötigen):

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

Der einfachste und zuverlässigste Weg ist der Versuch, so zu wirken, als Doubleob das Ergebnis nil- es kann nicht zu einer legitimen Zahl geformt werden.

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]

Weitere Informationen finden Sie in der Dokumentation: https://developer.apple.com/documentation/swift/double/2926277-init

Ariel
quelle
0

Simple hat die String-Erweiterung geschrieben:

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