Выяснение, является ли строка числовой или нет


93

Как мы можем проверить, состоит ли строка только из чисел? Я беру подстроку из строки и хочу проверить, числовая это подстрока или нет.

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

Ответы:


241

Вот один из способов, который не зависит от ограниченной точности попытки проанализировать строку как число:

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

Смотрите +[NSCharacterSet decimalDigitCharacterSet]и -[NSString rangeOfCharacterFromSet:].


6
это верно для @ "231.123", поэтому: // newString состоит только из цифр от 0 до 9 и .символа
Nicolas Tyler

5
NSMutableCharacterSet * digitsAndDots = [NSMutableCharacterSet decimalDigitCharacterSet]; [digitsAndDots addCharactersInString: @ "."]; NSCharacterSet * notDigitsNorDots = [digitsAndDots InvertedSet]; // также благодарим за добавление "InvertedSet". Я не знал о его существовании
codrut

5
примечание: этот метод считает @ "" числом; если возможно, вам может потребоваться также проверить [newString lenght]> 0. Пожалуйста, дайте мне знать, если я ошибаюсь.
markckim

2
@Supertecnoboff Похоже, в IOS что-то изменилось. Это можно использовать вместо этого, чтобы быть уверенным:[[NSCharacterSet characterSetWithCharactersInString:@"0123456789."] invertedSet]]
Николас Тайлер,

2
@KMGorbunov Я не думаю, что NSCharacterSet на самом деле хранит резервный набор, содержащий каждый отдельный символ в наборе. Я подозреваю, что invertedSetон возвращает класс, который обертывает созданный им набор и возвращает противоположное значение для всех запросов. Но точно не знаю.
Джон Калсбек

33

Я бы предложил использовать numberFromString:метод из класса NSNumberFormatter , как если бы число недействительно, оно вернет nil; в противном случае он вернет вам NSNumber.

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

Вы уверены, что он не вернет правильный номер, например, для @ "124sbd"?
Tommy

Нет, NOдля вашей строки вернутся и NSNumberFormatter, и NSScanner . Также стоит знать: они также возвращаются YESдля чисел, заполненных пробелами. Кстати, я только что добавил к вашему ответу фрагмент кода, надеюсь, вы не против.
Regexident

NSScanner должен вернуть «YES» для этой строки, согласно документации, найдя «действительное целочисленное представление». Более того, именно это и произошло в тесте на iOS 4.3.2. Однако NSNumberFormatter вернул ноль.
Томми

Это не ловит "." что является требованием для меня. Ответ от @John Calsbeek работал хорошо.
Дамиан

Что происходит со строкой, состоящей из сотен символов и цифр? Есть ли ограничение на созданный NSNumber?
Biniou

11

Проверяйте по регулярному выражению, по шаблону "^[0-9]+$", используя следующий метод -validateString:withPattern:.

[self validateString:"12345" withPattern:"^[0-9]+$"];
  1. Если "123.123" считается
    • С рисунком "^[0-9]+(.{1}[0-9]+)?$"
  2. Если ровно 4-х значные числа, без ".".
    • С рисунком "^[0-9]{4}$".
  3. Если цифры без цифр ".", а длина от 2 до 5.
    • С рисунком "^[0-9]{2,5}$".
  4. Со знаком минус: "^-?\d+$"

Регулярное выражение можно проверить на онлайн-сайте .

Вспомогательная функция выглядит следующим образом.

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

Тест на детской площадке.

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)

Я пробовал это для 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 }И получил ошибкуPlayground execution aborted: error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
Mathi Arasan

Я не знаю, какой правильный вариант для swift 3. Поправьте меня, если я ошибаюсь.
Мати Арасан

Как насчет того, чтобы простоreturn matchRange.location != NSNotFound;
LimeRed

как насчет знака минус?
Томас

@Thomas With ^-?\d+$, я проверил это на сайте: regex101.com
AechoLiu

9

Вы можете создать NSScanner и просто просканировать строку:

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

Ознакомьтесь с документацией NSScanner, чтобы выбрать из дополнительных методов.


Разве это не скажет вам, является ли хотя бы первый символ числовым?
Tommy

Виноват. Забыл последний звонок isAtEnd.
Regexident

6

Я думаю, что самый простой способ проверить, что каждый символ в данной строке является числовым:

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

Используйте один из других фабричных методов NSCharacterSet, если вы хотите полностью контролировать допустимые символы.


Мне это нравится. Хотя это не кажется очень чистым, это очень простой и удобный для основы способ проверки наличия "нецифровых" в NSString. + Я думаю, что это намного быстрее, чем любые другие способы, основанные на роботах (хотя мы, вероятно, здесь не особо обращаем внимание на производительность).
Nembleton

Я считаю, что stringByTrimmingCharactersInSet обрезает только начало и конец строки
Броди Робертсон

@BrodyRobertson: это так. Что не имеет никакого значения для этого ответа. Как вы думаете, для какого примера этот тест не удался?
Tommy

@Tommy, после переоценки я понял ваш ответ, он действителен, и вы проголосуете, но вам нужно, чтобы вы отредактировали, чтобы я мог проголосовать повторно
Броди Робертсон

6

Этот первоначальный вопрос касался Objective-C, но он также был задан за много лет до анонса Swift. Итак, если вы пришли сюда из Google и ищете решение, использующее Swift, то вот вам:

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

Решение Swift 3, если необходимо убедиться, что строка состоит только из цифр:

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

1
Чисто и красиво, лучшее решение. Мне нравится, что при этом не делается лишних .invertedи других действий.
Dannie P

4

чтобы было ясно, это функции для целых чисел в строках.

вот небольшая вспомогательная категория, основанная на ответе Джона выше:

в файле .h

@interface NSString (NumberChecking)

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

@end

в файле .m

#import "NSString+NumberChecking.h"

@implementation NSString (NumberChecking)

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

@end

использование:

#import "NSString+NumberChecking.h"

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

4

Решение Swift 3 может быть таким:

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

Ответ Джона Калсбека почти правильный, но опускает некоторые крайние случаи Unicode.

Согласно документацииdecimalDigitCharacterSet , этот набор включает все символы, отнесенные к категории Unicode как Nd. Таким образом, их ответ будет принимать, среди прочего:

  • (U + 0967 DEVANAGARI DIGIT ONE)
  • (U + 1811 MONGOLIAN DIGIT ONE)
  • 𝟙 (U + 1D7D9 МАТЕМАТИЧЕСКАЯ ДВОЙНАЯ СТРУКТУРА ЦИФРА ОДИН)

Хотя в некотором смысле это правильно - каждый символ в Ndдействительно соответствует десятичной цифре - это почти наверняка не то, что ожидал спрашивающий. На момент написания этой статьи существует 610 кодовых точек, отнесенных к категорииNd , только десять из которых являются ожидаемыми символами 0(от U + 0030) до 9(U + 0039).

Чтобы решить проблему, просто укажите именно те символы, которые допустимы:

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

1

Еще один вариант:

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

Пример использования:

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

1

Расширение ответа @John Calsbeek и разъяснение комментариев @Jeff и @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;
}

Следующие элементы могут быть добавлены как методы категорий для 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

Проверьте, является ли строка числом. Может быть полезно

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

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

0

Быстрое расширение:

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

Для Swift 3

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

0

Когда у вас есть цифры из смешанных языков , которые используют (или не используют) форматы цифр 0-9, вам нужно будет запустить регулярное выражение, которое будет искать любое число, следующее - преобразовать все цифры в 0- 9 формат (если вам нужно фактическое значение):

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

Самый простой и надежный способ - это попытаться преобразовать как Double, если результат будет nil- он не может быть преобразован в правильное число.

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]

Дополнительная информация в документации: https://developer.apple.com/documentation/swift/double/2926277-init


0

Simple написал расширение строки:

extension String {
    var isNumeric: Bool {
        return self.rangeOfCharacter(from: CharacterSet.decimalDigits.inverted) == nil
    }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.