7

考虑到本地化数字格式,如何检查字符串是否包含有效数字。

这个问题不是关于转换数字的主要问题。我可以用 NSNumberFormatter 做到这一点。如果我不能,那么我什至不需要这样做,并且可以将字符串保留为字符串。但我需要检查它是否包含有效的数字。

顺便说一句,我们在 textField:shouldChangeCharactersInRange: ... 委托方法的中间。在这里,我想通过返回 NO 来防止键入非法字符。

这是我拥有的代码:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {

    // Only test for numbers if it is a certain text field. 
    if (textField == self.numValueTextField) {
        NSString *resultingString = [textField.text stringByReplacingCharactersInRange: range withString: string];

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

        double holder;

        NSScanner *scan = [NSScanner scannerWithString: resultingString];

        BOOL isNumeric = [scan scanDouble: &holder] && [scan isAtEnd];

        if (isNumeric) {

            [self.detailItem setValue:number forKey:kValueDouble];
        }

        return isNumeric;
    }
    return YES;  // default for any other text field - if any.
}

这很好用,但它暗示英文符号。这意味着浮点必须是一个pont。但在世界的某些地方,它可能是一个逗号或其他任何东西。

我确实知道如何检查某些字符。所以我可以检查 0-9、逗号和点。但是有没有一种“正确”的方式来做到这一点?

如果它很重要:我在 iOS 环境中。

4

4 回答 4

8

默认情况下,数字格式化程序将使用当前语言环境的信息,因此它应该能够为您执行此检查。只需尝试格式化字符串。如果结果是nil,那么它是无效的。

static NSNumberFormatter * formatter = [NSNumberFormatter new];
//... build string
NSNumber * num = [formatter numberFromString:resultingString];
BOOL isNumeric = (num != nil);

例如:

NSNumberFormatter * formatter = [NSNumberFormatter new];

// US uses period as decimal separator
[formatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];

NSLog(@"%@", [formatter numberFromString:@"3.1415"]);   // Valid NSNumber
NSLog(@"%@", [formatter numberFromString:@"3.14.15"]);  // nil
NSLog(@"%@", [formatter numberFromString:@"31415"]);    // Valid
NSLog(@"%@", [formatter numberFromString:@"3,1415"]);   // nil

// Italy uses a comma as decimal separator
[formatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"it"]];
NSLog(@"%@", [formatter numberFromString:@"3.1415"]);    // nil
NSLog(@"%@", [formatter numberFromString:@"3.14.15"]);   // nil
NSLog(@"%@", [formatter numberFromString:@"31415"]);     // Valid
NSLog(@"%@", [formatter numberFromString:@"3,1415"]);    // Valid

或者您可能更喜欢使用getObjectValue:forString:range:error:,它只会返回YESNO指示解析成功,如果您对更多细节感兴趣,还会给您一个错误对象。

于 2013-03-11T22:43:46.653 回答
3

我就是这样做的。您想要的关键信息是[[NSLocale currentLocale] objectForKey:NSLocaleDecimalSeparator],它将告诉您当前的小数分隔符。然后你寻找任何不在合法集中的字符。

- (BOOL)isLegalDigitString:(NSString *)string
              forTextField:(UITextField *)textField
           hasDecimalPoint:(BOOL)hasDecimalPoint
{
  // It's always legal to delete
  if ([string length] == 0)
  {
    return YES;
  }

  // Only legal characters
  NSString *decimalSeparator = [[NSLocale currentLocale] objectForKey:NSLocaleDecimalSeparator];

  NSString *legalCharacters = [@"1234567890" stringByAppendingString:
                               (hasDecimalPoint ? decimalSeparator
                                : @"")];
  NSCharacterSet *forbiddenCharacterSet = [[NSCharacterSet characterSetWithCharactersInString:legalCharacters] invertedSet];
  if ([string rangeOfCharacterFromSet:forbiddenCharacterSet].location != NSNotFound)
  {
    return NO;
  }

  // Only one decimal point
  if (hasDecimalPoint &&
      [string rangeOfString:decimalSeparator].location != NSNotFound &&
      [[textField text] rangeOfString:decimalSeparator].location != NSNotFound)
  {
    return NO;
  }

  return YES;
}

作为我如何使用它的一个例子:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
  if (textField == [self integerTextField])
  {
    return [self isLegalDigitString:string forTextField:textField hasDecimalPoint:NO];
  }
  else if (textField == [self floatTextField])
  {
    return [self isLegalDigitString:string forTextField:textField hasDecimalPoint:YES];
  }

  return YES;
}

这做得不好的一件事是管理有限的精度(例如,一个只能有十分之一的字段)。它需要一些重新设计。

于 2013-03-11T22:26:12.263 回答
0

坦率地说,任何对有效数字的测试即使对部分数字也应该有效,实施起来非常棘手。

  1. 不要试图重新实现已经在NSNumberFormatter. 不同的语言使用不同的小数分隔符,但也使用不同的数字(编辑:不幸的是,NSNumberFormatter也无法验证非阿拉伯数字)。

  2. 有许多特殊情况不是有效数字,但可以成为有效数字,例如空字符串、减号、小数分隔符、减号与小数分隔符的组合。如果你允许科学记数法,它会变得更加复杂。

最简单的解决方案是只检查最终数字。当用户确认时检查输入(点击进入,点击输入视图之外)。

但是,还要注意NSNumberFromatter实际上可以验证部分字符串。有关更多信息,请参阅其超类NSFormatter

于 2014-05-06T22:47:42.807 回答
0

您可以使用 NSLocale 获取本地化的分组分隔符,然后将其从字符串中删除,因为 NSScanner 不处理分组。

NSString *sep = [[NSLocale currentLocale] objectForKey:NSLocaleGroupingSeparator];
NSString *string2 = [string1 stringByReplacingOccurrencesOfString:sep withString:@""];

初始化一个考虑本地化小数分隔符的 NSScanner。

NSScanner *scan = [NSScanner localizedScannerWithString:string2];

然后继续使用您的 NSScanner 代码。使用 NSScanner 比手动编码的方法更健壮,因为它可以处理所有符合 IEEE 标准的数字,包括 +/- 和科学记数法。它也更短。

于 2014-05-06T22:25:31.977 回答