检查UITextField的输入是否仅为数字

82

我如何验证UITextField的字符串输入?我想检查字符串是否为数字,包括小数点。

22个回答

118

你可以像这样用几行代码实现:

BOOL valid;
NSCharacterSet *alphaNums = [NSCharacterSet decimalDigitCharacterSet];
NSCharacterSet *inStringSet = [NSCharacterSet characterSetWithCharactersInString:myInputField.text];
valid = [alphaNums isSupersetOfSet:inStringSet];    
if (!valid) // Not numeric

-- 该部分用于验证输入是否仅为数字字符。查看NSCharacterSet文档以了解其他选项。您可以使用characterSetWithCharactersInString方法来指定任何一组有效的输入字符。


22
这个答案未检查重复或标点符号。 - zambono

72
有几种方法可以实现这个功能:
  1. 使用NSNumberFormatter的numberFromString:方法。如果能正确解析字符串,则返回NSNumber,否则返回nil
  2. 使用NSScanner
  3. 去除任何非数字字符并查看字符串是否仍然匹配
  4. 使用正则表达式
在我看来,使用类似于-[NSString doubleValue]的方法可能不是最好的选择,因为@"0.0"@"abc"都具有0的doubleValue。 *value方法如果无法正确转换字符串,则全部返回0,因此难以区分合法的@"0"字符串和非有效的字符串。类似于C的strtol函数也会出现相同的问题。
我认为使用NSNumberFormatter会是最好的选择,因为它考虑了地区设置(例如,在欧洲使用的数字@"1,23"与在美国使用的数字@"1.23")。

5
@Tony,你好。我不确定你在做什么,因为对于我来说,下面的日志输出"N: (null)":NSNumberFormatter * f = [[NSNumberFormatter alloc] init]; NSNumber * n = [f numberFromString:@"34jfkjdskj80"]; NSLog(@"N: %@", n);。现在我给你翻译了原文,请查看。 - Dave DeLong
2
这可能有点老了,但如果有人在阅读这篇文章,我认为值得注意的是,像 NSNumberFormatter 一样,NSScanner 在解析字符串时会考虑区域设置,只要您在扫描器对象上使用 setLocale:(例如,您可以提供 [NSLocale currentLocale])。 - dreamlax
1
有些人喜欢读《战争与和平》,而另一些人则更喜欢《白鲸记》。我个人认为,我只需要下载Stack Overflow数据集,过滤掉Dave回答过的问题,然后就可以愉快地享受了。 - BP.
“- (NSNumber *)numberFromString:”没有记录在错误时返回“nil”。(请参见https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSNumberFormatter_Class/Reference/Reference.html)。我们可以依靠这种行为吗? - Mark Amery
1
@MarkAmery 是的,你可以依赖这种行为。虽然它可能没有被记录下来,但这是多年来一直表现出来的方式,从二进制兼容性的角度来看,改变它是不可行的。 - Dave DeLong
显示剩余7条评论

35

我在我的Mac应用程序中使用这段代码,同样或相似的代码应该也可以在iPhone上使用。它基于RegexKitLite正则表达式,并在文本无效时将其变为红色。

static bool TextIsValidValue( NSString* newText, double &value )
{
    bool result = false;

    if ( [newText isMatchedByRegex:@"^(?:|0|[1-9]\\d*)(?:\\.\\d*)?$"] ) {
        result = true;
        value = [newText doubleValue];
    }
    return result;
}

- (IBAction) doTextChanged:(id)sender;
{
    double value;
    if ( TextIsValidValue( [i_pause stringValue], value ) ) {
        [i_pause setTextColor:[NSColor blackColor]];
        // do something with the value
    } else {
        [i_pause setTextColor:[NSColor redColor]];
    }
}

7
是的,我知道这个回答非常老了,但是我认为静态方法应该像这样声明:+ (BOOL)TextIsValidValue:(NSString *)newText :(double *)value... - RCIX
7
TextIsValidValue是一个静态的C函数,这意味着它是局部于文件中的,并不属于任何对象。你(@RCIX)正在描述一个对象方法,它类似于C++中的静态方法,但与静态C函数(或静态变量)完全不同! - Peter N Lewis
该表达式匹配空字符串(这不是数字)。 - Nikolai Ruhe
通常,为了匹配输入的数字,您需要允许空字符串,否则用户无法输入“1<delete>2”等内容。 - Peter N Lewis
6
对于那些不知道什么是“正则表达式”或者它们如何工作的开发人员,可以前往 这个正则表达式解码器网站,并复制/粘贴 (?:|0|[1-9]\\d*)(?:\\.\\d*) 以查看它的含义。此外,可以参考正则表达式维基百科页面 - mfaani
regex101.com非常好,但请注意regex101.com说:“警告:空替代实际上使该组可选,这表明替代是完全冗余的”,这是完全不正确的,因为在-之前有^ - 替代指定没有任何内容,0或1-9后跟小数点之前的零个或多个数字。它肯定不是可选的,尽管您可能根据自己的喜好选择禁止“.5”。 - Peter N Lewis

19

如果您希望用户只能输入数字,您可以使您的视图控制器实现UITextFieldDelegate的一部分,并定义此方法:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
  NSString *resultingString = [textField.text stringByReplacingCharactersInRange: range withString: string];

  // The user deleting all input is perfectly acceptable.
  if ([resultingString length] == 0) {
    return true;
  }

  NSInteger holder;

  NSScanner *scan = [NSScanner scannerWithString: resultingString];

  return [scan scanInteger: &holder] && [scan isAtEnd];
}

可能有更高效的方法,但我觉得这是一种相当方便的方式。而且该方法应该可以很容易地适应验证双精度或其他内容:只需使用scanDouble:或类似方法即可。


13
#pragma mark - UItextfield Delegate

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    if ([string isEqualToString:@"("]||[string isEqualToString:@")"]) {
        return TRUE;
    }

    NSLog(@"Range ==%d  ,%d",range.length,range.location);
    //NSRange *CURRANGE = [NSString  rangeOfString:string];

    if (range.location == 0 && range.length == 0) {
        if ([string isEqualToString:@"+"]) {
            return TRUE;
        }
    }
    return [self isNumeric:string];
}

-(BOOL)isNumeric:(NSString*)inputString{
    BOOL isValid = NO;
    NSCharacterSet *alphaNumbersSet = [NSCharacterSet decimalDigitCharacterSet];
    NSCharacterSet *stringSet = [NSCharacterSet characterSetWithCharactersInString:inputString];
    isValid = [alphaNumbersSet isSupersetOfSet:stringSet];
    return isValid;
}

11
这里有一些简短的语句,结合了Peter Lewis在上面回答的内容 (检查UITextField输入是否为数字) 和 NSPredicates。
    #define REGEX_FOR_NUMBERS   @"^([+-]?)(?:|0|[1-9]\\d*)(?:\\.\\d*)?$"
    #define REGEX_FOR_INTEGERS  @"^([+-]?)(?:|0|[1-9]\\d*)?$"
    #define IS_A_NUMBER(string) [[NSPredicate predicateWithFormat:@"SELF MATCHES %@", REGEX_FOR_NUMBERS] evaluateWithObject:string]
    #define IS_AN_INTEGER(string) [[NSPredicate predicateWithFormat:@"SELF MATCHES %@", REGEX_FOR_INTEGERS] evaluateWithObject:string]

请提供一个链接,让我可以了解更多相关信息。 - Smit Saraiya

4

对于整数测试,它将是:

- (BOOL) isIntegerNumber: (NSString*)input
{
    return [input integerValue] != 0 || [input isEqualToString:@"0"];
}

不错的答案,但是这种方法仍然存在一个问题,就是将输入值“232asdfsadf”视为数字。 - Whoami
你可以将 integerValue(重新转换为字符串)与原始字符串进行长度比较,以确保“123aoenuthr”不被视为整数。 - ThePrimeagen

3
在我看来,实现您的目标最好的方法是显示数字键盘而不是普通键盘。这限制了用户可以使用哪些键。这样可以减轻验证的需要,更重要的是它可以防止用户犯错误。数字键盘也更适合输入数字,因为键盘的按键明显更大。
在界面生成器中选择UITextField,转到属性检查器并将“键盘类型”更改为“小数点键盘”。

enter image description here

那将使键盘看起来像这样:

enter image description here

唯一要做的就是确保用户不输入两个小数位。您可以在用户编辑时完成此操作。将以下代码添加到您的视图控制器中。该代码会在第二个小数位被输入时立即将其删除。对用户来说,似乎第二个小数位从未出现过。
- (void)viewDidLoad
{
  [super viewDidLoad];

  [self.textField addTarget:self
                    action:@selector(textFieldDidChange:)
           forControlEvents:UIControlEventEditingChanged];
}

- (void)textFieldDidChange:(UITextField *)textField
{
  NSString *text = textField.text;
  NSRange range = [text rangeOfString:@"."];

  if (range.location != NSNotFound &&
      [text hasSuffix:@"."] &&
      range.location != (text.length - 1))
  {
    // There's more than one decimal
    textField.text = [text substringToIndex:text.length - 1];
  }
}

1
不要忘记复制和粘贴的能力 - 在这种情况下,你的方法会失败。 - slxl
1
这并不涵盖所有情况。用户可能会复制粘贴文本或使用蓝牙键盘。 - bean

3
您可以使用字符串的doubleValue,例如:
NSString *string=@"1.22";
double a=[string doubleValue];

我认为如果字符串无效,这将返回0.0(它可能会抛出异常,在这种情况下,您可以捕获它,但文档中说是0.0)。更多信息在此处


通常情况下,使用库函数要比自己编写更好。在这种情况下,其他文化使用逗号作为小数点,而预计Cocoa库可以处理这一情况。 - kibibu
更不用说进行字面上的浮点数相等比较只是一种不好的做法了。 - M. Ryan

3

虽然有些晚了,但我这里有一个非常实用的小技巧,可以考虑到小数点和本地符号。链接到它的代码片段在这里

@interface NSString (Extension)

- (BOOL) isAnEmail;
- (BOOL) isNumeric;

@end

@implementation NSString (Extension)

/**
 *  Determines if the current string is a valid email address.
 *
 *  @return BOOL - True if the string is a valid email address.
 */

- (BOOL) isAnEmail
{
    NSString *emailRegex = @"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}";
    NSPredicate *emailTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", emailRegex];

    return [emailTest evaluateWithObject:self];
}

/**
 *  Determines if the current NSString is numeric or not. It also accounts for the localised (Germany for example use "," instead of ".") decimal point and includes these as a valid number.
 *
 *  @return BOOL - True if the string is numeric.
 */

- (BOOL) isNumeric
{
    NSString *localDecimalSymbol = [[NSLocale currentLocale] objectForKey:NSLocaleDecimalSeparator];
    NSMutableCharacterSet *decimalCharacterSet = [NSMutableCharacterSet characterSetWithCharactersInString:localDecimalSymbol];
    [decimalCharacterSet formUnionWithCharacterSet:[NSCharacterSet alphanumericCharacterSet]];

    NSCharacterSet* nonNumbers = [decimalCharacterSet invertedSet];
    NSRange r = [self rangeOfCharacterFromSet: nonNumbers];

    if (r.location == NSNotFound)
    {
        // check to see how many times the decimal symbol appears in the string. It should only appear once for the number to be numeric.
        int numberOfOccurances = [[self componentsSeparatedByString:localDecimalSymbol] count]-1;
        return (numberOfOccurances > 1) ? NO : YES;
    }
    else return NO;
}

@end

在isNumeric中,alphanumericCharacterSet不应该是decimalDigitCharacterSet吗?这是我的用法。 - Andrei Radulescu
不起作用,@"A" 得到的结果是Numeric YES,但它不是一个数字。 - Gank

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接